├── .github ├── dependabot.yml └── workflows │ ├── linux.yml │ └── windows.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── libomemo.pc.cmake ├── src ├── libomemo.c ├── libomemo.h ├── libomemo_crypto.c ├── libomemo_crypto.h ├── libomemo_storage.c └── libomemo_storage.h └── test ├── test_crypto.c ├── test_libomemo.c └── test_storage.c /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Sebastian Pipping 2 | # Licensed under the GPL v2 or later 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | commit-message: 9 | include: "scope" 10 | prefix: "Actions" 11 | directory: "/" 12 | labels: 13 | - "enhancement" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Sebastian Pipping 2 | # Licensed under the GPL v2 or later 3 | 4 | name: Build for Linux 5 | 6 | on: 7 | pull_request: 8 | push: 9 | schedule: 10 | - cron: '0 2 * * 5' # Every Friday at 2am 11 | 12 | jobs: 13 | checks: 14 | name: Build for Linux (shared=${{ matrix.BUILD_SHARED_LIBS }}) 15 | runs-on: ubuntu-20.04 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | BUILD_SHARED_LIBS: ['ON', 'OFF'] 20 | steps: 21 | 22 | - uses: actions/checkout@v3.0.2 23 | 24 | - name: Install build dependencies 25 | run: |- 26 | set -x 27 | sudo apt-get update 28 | sudo apt-get install --yes --no-install-recommends -V \ 29 | gcovr \ 30 | libcmocka-dev \ 31 | libgcrypt20-dev \ 32 | libglib2.0-dev \ 33 | libmxml-dev \ 34 | libsqlite3-dev \ 35 | ninja-build 36 | 37 | - name: Configure 38 | run: |- 39 | set -x 40 | cmake \ 41 | -B build \ 42 | -G Ninja \ 43 | -DBUILD_SHARED_LIBS=${{ matrix.BUILD_SHARED_LIBS }} \ 44 | -D_OMEMO_WITH_COVERAGE=ON 45 | 46 | - name: Build 47 | run: |- 48 | set -x 49 | ninja -v -C build all 50 | cat build/libomemo.pc 51 | 52 | - name: Test 53 | run: |- 54 | set -x 55 | CTEST_OUTPUT_ON_FAILURE=1 ninja -C build test 56 | ninja -C build coverage 57 | 58 | - name: Install 59 | run: |- 60 | set -x -o pipefail 61 | DESTDIR="${PWD}"/ROOT ninja -v -C build install 62 | find ROOT/ -not -type d | sort | xargs ls -l 63 | 64 | - name: Store coverage HTML report 65 | uses: actions/upload-artifact@v3.1.0 66 | with: 67 | name: omemo_coverage_shared_${{ matrix.BUILD_SHARED_LIBS }} 68 | path: build/coverage*.html 69 | if-no-files-found: error 70 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Sebastian Pipping 2 | # Licensed under the GPL v2 or later 3 | 4 | name: Build for Windows 5 | 6 | on: 7 | pull_request: 8 | push: 9 | schedule: 10 | - cron: '0 2 * * 5' # Every Friday at 2am 11 | 12 | jobs: 13 | checks: 14 | name: Build for Windows (shared=${{ matrix.BUILD_SHARED_LIBS }}) 15 | runs-on: windows-2019 16 | defaults: 17 | run: 18 | shell: msys2 {0} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | BUILD_SHARED_LIBS: ['ON', 'OFF'] 23 | steps: 24 | 25 | - uses: actions/checkout@v3.0.2 26 | 27 | - name: Install build dependencies 28 | uses: msys2/setup-msys2@v2 29 | with: 30 | msystem: MINGW32 31 | install: | 32 | cmake 33 | mingw-w64-i686-cmocka 34 | mingw-w64-i686-glib2 35 | mingw-w64-i686-libgcrypt 36 | mingw-w64-i686-mxml 37 | mingw-w64-i686-sqlite3 38 | mingw-w64-i686-toolchain 39 | ninja 40 | 41 | - name: Configure 42 | run: |- 43 | set -x 44 | cmake \ 45 | -B build \ 46 | -G Ninja \ 47 | -DCMAKE_C_COMPILER=i686-w64-mingw32-gcc -DCMAKE_SYSTEM_NAME=Windows -DWIN32=ON -DMINGW=ON \ 48 | -DBUILD_SHARED_LIBS=${{ matrix.BUILD_SHARED_LIBS }} \ 49 | -D_OMEMO_WITH_COVERAGE=ON 50 | 51 | - name: Build 52 | run: |- 53 | set -x 54 | ninja -v -C build all 55 | cat build/libomemo.pc 56 | 57 | - name: Test 58 | run: |- 59 | set -x 60 | CTEST_OUTPUT_ON_FAILURE=1 ninja -C build test 61 | # Note: msys2 does not come with a package for gcovr, yet(?) 62 | # ninja -C build coverage 63 | 64 | - name: Install 65 | run: |- 66 | set -x -o pipefail 67 | DESTDIR="${PWD}"/ROOT ninja -v -C build install 68 | find ROOT/ -not -type d | sort | xargs ls -l 69 | 70 | - name: Store Windows binaries 71 | uses: actions/upload-artifact@v3.1.0 72 | with: 73 | name: omemo_win32bin_shared_${{ matrix.BUILD_SHARED_LIBS }} 74 | path: | 75 | build/*.a 76 | build/*.dll 77 | build/*.exe 78 | if-no-files-found: error 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | coverage/ 4 | *.gc* 5 | *.o 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.8.1] - 2022-04-10 8 | ### Added 9 | - In some error cases additional information is printed to `stderr` if `LIBOMEMO_DEBUG` is set ([#40](https://github.com/gkdr/libomemo/pull/40)) 10 | 11 | ### Fixed 12 | - Fix default prefix to be `/usr/local` rather than `/` ([#38](https://github.com/gkdr/libomemo/pull/38)) (thanks, [@hartwork](https://github.com/hartwork)!) 13 | - Fix filed `Requires.private` and `Requires` in auto-generated pkg-config file `libomemo.pc` ([#38](https://github.com/gkdr/libomemo/pull/38)) (thanks, [@hartwork](https://github.com/hartwork)!) 14 | 15 | ### Changed 16 | - Migrate build system from a Makefile to CMake ([#38](https://github.com/gkdr/libomemo/pull/38)) (thanks, [@hartwork](https://github.com/hartwork)!) 17 | - Most XML parsing errors should have a discernible error code now, mostly replacing the more general ones ([#40](https://github.com/gkdr/libomemo/pull/40)) 18 | 19 | ### Infrastructure 20 | - Cover Windows build by GitHub Actions CI using msys2 ([#38](https://github.com/gkdr/libomemo/pull/38)) (thanks, [@hartwork](https://github.com/hartwork)!) 21 | - dependabot: update actions/checkout from 2.4.0 to 3.0.0 ([#41](https://github.com/gkdr/libomemo/pull/41)) 22 | - dependabot: update actions/upload-artifact from 2.3.1 to 3.0.0 ([#42](https://github.com/gkdr/libomemo/pull/42)) 23 | 24 | ## [0.8.0] - 2022-02-14 25 | ### Added 26 | - It is now possible to add a key with a `prekey` attribute. ([#28](https://github.com/gkdr/libomemo/issues/28)) 27 | - A function to check via the attribute whether a received key is a prekey. 28 | - Mention in the README the exact version implemented. ([#26](https://github.com/gkdr/libomemo/issues/26)) 29 | 30 | ### Removed 31 | - It is not any longer possible to set the used XML namespace at build time. ([#21](https://github.com/gkdr/libomemo/issues/21)) 32 | 33 | ### Fixed 34 | - Added missing symlinks for the `.so` files. ([#34](https://github.com/gkdr/libomemo/pull/34)) (thanks, [@hartwork](https://github.com/hartwork)!) 35 | - Fix crossbuild using wrong multiarch triplet. ([#36](https://github.com/gkdr/libomemo/pull/36)) (thanks, [@fortysixandtwo](https://github.com/fortysixandtwo)!) 36 | - Flaky test `test_aes_gcm_encrypt_decrypt`. ([#39](https://github.com/gkdr/libomemo/issues/39)) (thanks, [@hartwork](https://github.com/hartwork)!) 37 | 38 | ### Infrastructure 39 | - Cover Linux build by GitHub Actions CI ([#37](https://github.com/gkdr/libomemo/pull/37)) (thanks, [@hartwork](https://github.com/hartwork)!) 40 | 41 | ## [0.7.1] - 2021-01-31 42 | ### Fixed 43 | - Test file cleanup now won't randomly break parallel builds. ([#31](https://github.com/gkdr/libomemo/pull/31)) (thanks, [@fortysixandtwo](https://github.com/fortysixandtwo)!) 44 | 45 | ## [0.7.0] - 2020-12-02 46 | ### Added 47 | - This file. 48 | - Makefile target for building a shared library. ([#30](https://github.com/gkdr/libomemo/pull/30)) (thanks, [@fortysixandtwo](https://github.com/fortysixandtwo)!) 49 | - `.vscode` dir to `.gitignore` 50 | 51 | ### Changed 52 | - The build now includes debug symbols. 53 | - Generated IVs for outgoing messages are now 12 bytes long. This should help compatibility with Monal. ([#24](https://github.com/gkdr/libomemo/issues/24)) (thanks, everyone!) 54 | 55 | ### Fixed 56 | - `omemo_message_create()` error handling ([#22](https://github.com/gkdr/libomemo/pull/22)) (thanks, [@msantos](https://github.com/msantos)!) 57 | - Wrongly expecting `` and `` elements to be in a certain order. This should help compatibility with Dino. 58 | - The link to _gcovr_ in the README. ([#29](https://github.com/gkdr/libomemo/pull/29)) (thanks, [@alucaes](https://github.com/aluaces)!) 59 | 60 | ## [0.6.2] and below 61 | lost to commit messages 62 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Sebastian Pipping 2 | # Licensed under the GPL v2 or later 3 | 4 | cmake_minimum_required(VERSION 3.16.3) 5 | 6 | project(omemo 7 | VERSION 8 | 0.8.1 9 | # NOTE: Because this^^ version affects shared library filenames, 10 | # it needs a major version bump to 1.0.0 already at 11 | # the _first ever ABI break_ despite semver rule 4 12 | # (https://semver.org/#spec-item-4). 13 | LANGUAGES 14 | C 15 | ) 16 | 17 | include(FindPkgConfig) 18 | include(GNUInstallDirs) 19 | 20 | 21 | # 22 | # Public configuration 23 | # 24 | option(BUILD_SHARED_LIBS "Build shared libraries (rather than static ones)" ON) 25 | option(OMEMO_INSTALL "Install build artifacts" ON) 26 | option(OMEMO_WITH_TESTS "Build test suite (depends on cmocka)" ON) 27 | if(NOT _OMEMO_HELP) # hide from "cmake -DOMEMO_HELP=ON -LH ." output 28 | option(_OMEMO_WARNINGS_AS_ERRORS "(Unofficial!) Turn warnings into errors" OFF) 29 | option(_OMEMO_WITH_COVERAGE "(Unofficial!) Build with coverage" OFF) 30 | endif() 31 | 32 | if(NOT BUILD_SHARED_LIBS) 33 | # NOTE: If we don't enforce -fPIC for static(!) libraries, we may run into 34 | # "[..] relocation R_X86_64_PC32 against symbol [..]" link errors 35 | # in dependent projects trying to link a shared library based on 36 | # our static library. 37 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 38 | endif() 39 | 40 | 41 | # 42 | # Global CPPFLAGS and CFLAGS 43 | # 44 | add_compile_definitions( 45 | _XOPEN_SOURCE=700 46 | _DEFAULT_SOURCE 47 | ) 48 | add_compile_options( 49 | -std=c99 50 | -Wall 51 | -Wextra 52 | -Wpedantic 53 | -Wstrict-overflow 54 | -fno-strict-aliasing 55 | -funsigned-char 56 | -fno-builtin-memset 57 | ) 58 | 59 | if(_OMEMO_WARNINGS_AS_ERRORS) 60 | add_compile_options(-Werror) 61 | endif() 62 | 63 | if(_OMEMO_WITH_COVERAGE) 64 | set(_OMEMO_COVERAGE_FLAGS -g -O0 --coverage) 65 | add_compile_options(${_OMEMO_COVERAGE_FLAGS}) 66 | link_libraries(${_OMEMO_COVERAGE_FLAGS}) 67 | endif() 68 | 69 | 70 | # 71 | # Build dependencies 72 | # 73 | # NOTE: We cannot use "pkg_check_modules([..] IMPORTED_TARGET [..])" 74 | # because we'd run into a (false positive) CMake error 75 | # "contains relative path in its INTERFACE_INCLUDE_DIRECTORIES" 76 | # when using "target_link_libraries([..] PkgConfig::[..])" with msys2. 77 | if(OMEMO_WITH_TESTS) 78 | pkg_check_modules(CMOCKA REQUIRED "cmocka") 79 | endif() 80 | pkg_check_modules(GLIB REQUIRED "glib-2.0") 81 | pkg_check_modules(GCRYPT REQUIRED "libgcrypt") 82 | pkg_check_modules(MXML REQUIRED "mxml") 83 | pkg_check_modules(SQLITE REQUIRED "sqlite3") 84 | 85 | 86 | # 87 | # C library 88 | # 89 | file(GLOB _OMEMO_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/libomemo*.[ch]) 90 | add_library(omemo ${_OMEMO_SOURCES}) 91 | target_include_directories(omemo PUBLIC $) 92 | 93 | if(OMEMO_INSTALL) 94 | file(GLOB _OMEMO_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/libomemo*.h) 95 | target_include_directories(omemo PUBLIC $) 96 | install(FILES ${_OMEMO_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libomemo) 97 | install(TARGETS omemo EXPORT omemo 98 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 99 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 100 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 101 | ) 102 | endif() 103 | 104 | if(NOT WIN32) 105 | set_property(TARGET omemo PROPERTY VERSION ${PROJECT_VERSION}) 106 | set_property(TARGET omemo PROPERTY SOVERSION ${PROJECT_VERSION_MAJOR}) 107 | set_property(TARGET omemo PROPERTY NO_SONAME ${NO_SONAME}) 108 | endif() 109 | 110 | 111 | # 112 | # pkg-config/pkgconf file 113 | # 114 | set(_OMEMO_PKGCONF_EXEC_PREFIX ${CMAKE_INSTALL_PREFIX}) 115 | set(_OMEMO_PKGCONF_LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR}) 116 | set(_OMEMO_PKGCONF_INCLUDEDIR ${CMAKE_INSTALL_FULL_INCLUDEDIR}) 117 | set(_OMEMO_PKGCONF_PREFIX ${CMAKE_INSTALL_PREFIX}) 118 | string(REPLACE ${CMAKE_INSTALL_PREFIX} \${exec_prefix} _OMEMO_PKGCONF_LIBDIR ${_OMEMO_PKGCONF_LIBDIR}) 119 | string(REPLACE ${CMAKE_INSTALL_PREFIX} \${prefix} _OMEMO_PKGCONF_EXEC_PREFIX ${_OMEMO_PKGCONF_EXEC_PREFIX}) 120 | string(REPLACE ${CMAKE_INSTALL_PREFIX} \${prefix} _OMEMO_PKGCONF_INCLUDEDIR ${_OMEMO_PKGCONF_INCLUDEDIR}) 121 | 122 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libomemo.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libomemo.pc @ONLY) 123 | set_target_properties(omemo PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/libomemo.pc) 124 | 125 | if(OMEMO_INSTALL) 126 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libomemo.pc 127 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig 128 | ) 129 | endif() 130 | 131 | 132 | # 133 | # C test suite 134 | # 135 | if(OMEMO_WITH_TESTS) 136 | set(_OMEMO_TEST_TARGETS test_crypto test_libomemo test_storage) 137 | 138 | enable_testing() 139 | 140 | foreach(_target ${_OMEMO_TEST_TARGETS}) 141 | add_executable(${_target} ${CMAKE_CURRENT_SOURCE_DIR}/test/${_target}.c) 142 | target_link_libraries(${_target} PRIVATE omemo) 143 | add_test(NAME ${_target} COMMAND ${_target}) 144 | 145 | if(BUILD_SHARED_LIBS) 146 | target_compile_options(${_target} PRIVATE ${CMOCKA_CFLAGS}) 147 | target_link_libraries(${_target} PRIVATE ${CMOCKA_LIBRARIES}) 148 | else() 149 | target_compile_options(${_target} PRIVATE ${CMOCKA_STATIC_CFLAGS}) 150 | target_link_libraries(${_target} PRIVATE ${CMOCKA_STATIC_LIBRARIES}) 151 | endif() 152 | endforeach() 153 | endif() 154 | 155 | 156 | # 157 | # External build dependencies 158 | # 159 | foreach(_target omemo ${_OMEMO_TEST_TARGETS}) 160 | if(BUILD_SHARED_LIBS) 161 | # TODO: Tests should stop depending on gcrypt 162 | # once the tests stop including libomemo's .c(!) files 163 | target_compile_options(${_target} PRIVATE ${GCRYPT_CFLAGS}) 164 | target_link_libraries(${_target} PRIVATE ${GCRYPT_LIBRARIES}) 165 | 166 | target_compile_options(${_target} PUBLIC ${GLIB_CFLAGS}) 167 | target_link_libraries(${_target} PUBLIC ${GLIB_LIBRARIES}) 168 | 169 | # TODO: Tests should stop depending on mxml 170 | # once the tests stop including libomemo's .c(!) files 171 | target_compile_options(${_target} PRIVATE ${MXML_CFLAGS}) 172 | target_link_libraries(${_target} PRIVATE ${MXML_LIBRARIES}) 173 | 174 | target_compile_options(${_target} PRIVATE ${SQLITE_CFLAGS}) 175 | target_link_libraries(${_target} PRIVATE ${SQLITE_LIBRARIES}) 176 | else() 177 | # TODO: Tests should stop depending on gcrypt 178 | # once the tests stop including libomemo's .c(!) files 179 | target_compile_options(${_target} PRIVATE ${GCRYPT_STATIC_CFLAGS}) 180 | target_link_libraries(${_target} PRIVATE ${GCRYPT_STATIC_LIBRARIES}) 181 | 182 | target_compile_options(${_target} PUBLIC ${GLIB_STATIC_CFLAGS}) 183 | target_link_libraries(${_target} PUBLIC ${GLIB_STATIC_LIBRARIES}) 184 | 185 | # TODO: Tests should stop depending on mxml 186 | # once the tests stop including libomemo's .c(!) files 187 | target_compile_options(${_target} PRIVATE ${MXML_STATIC_CFLAGS}) 188 | target_link_libraries(${_target} PRIVATE ${MXML_STATIC_LIBRARIES}) 189 | 190 | target_compile_options(${_target} PRIVATE ${SQLITE_STATIC_CFLAGS}) 191 | target_link_libraries(${_target} PRIVATE ${SQLITE_STATIC_LIBRARIES}) 192 | endif() 193 | endforeach() 194 | 195 | 196 | # 197 | # Coverage reporting 198 | # 199 | if(_OMEMO_WITH_COVERAGE) 200 | add_custom_target(coverage 201 | COMMAND gcovr -r ${CMAKE_CURRENT_SOURCE_DIR} --html --html-details -o coverage.html 202 | COMMAND gcovr -r ${CMAKE_CURRENT_SOURCE_DIR} -s 203 | ) 204 | endif() 205 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Richard Bayerle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libomemo 0.8.1 2 | Implements [OMEMO](https://conversations.im/omemo/) ([XEP-0384 v0.3.0](https://xmpp.org/extensions/attic/xep-0384-0.3.0.html)) in C. 3 | 4 | Input and output are XML strings, so it does not force you to use a certain XML lib. 5 | While the actual protocol functions do not depend on any kind of storage, it comes with a basic implementation in SQLite. 6 | 7 | It deals with devicelists and bundles as well as encrypting the payload, but does not handle the double ratchet sessions for encrypting the key to the payload. 8 | However, you can use my [axc](https://github.com/gkdr/axc) lib for that and easily combine it with this one (or write all the libsignal client code yourself if that is better suited to your needs). 9 | 10 | ## Dependencies 11 | * CMake (`cmake`) 12 | * pkg-config (`pkg-config`) or pkgconf (`pkgconf`) 13 | * [Mini-XML](http://www.msweet.org/projects.php?Z3) (`libmxml-dev`) 14 | * gcrypt (`libgcrypt20-dev`) 15 | * glib (`libglib2.0-dev`) 16 | * SQLite (`libsqlite3-dev`) 17 | * GNU make (`make`) or Ninja (`ninja-build`) 18 | 19 | Optional: 20 | * [cmocka](https://cmocka.org/) (`libcmocka-dev`) for testing (`make test`) 21 | * [gcovr](http://gcovr.com/) (`gcovr`) for a coverage report (`make coverage`) 22 | 23 | ## Installation 24 | libomemo uses CMake as a build system. It can be used with either GNU make or Ninja. For example: 25 | 26 | ``` 27 | mkdir build 28 | cd build 29 | 30 | cmake -G Ninja .. # for options see below 31 | 32 | ninja -v all 33 | ninja -v test # potentially with CTEST_OUTPUT_ON_FAILURE=1 in the environment 34 | ninja -v install 35 | ``` 36 | 37 | The following configuration options are supported: 38 | 39 | ```console 40 | # rm -f CMakeCache.txt ; cmake -D_OMEMO_HELP=ON -LH . | grep -B1 ':.*=' | sed 's,^--$,,' 41 | // Build shared libraries (rather than static ones) 42 | BUILD_SHARED_LIBS:BOOL=ON 43 | 44 | // Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ... 45 | CMAKE_BUILD_TYPE:STRING= 46 | 47 | // Install path prefix, prepended onto install directories. 48 | CMAKE_INSTALL_PREFIX:PATH=/usr/local 49 | 50 | // Install build artifacts 51 | OMEMO_INSTALL:BOOL=ON 52 | 53 | // Build test suite (depends on cmocka) 54 | OMEMO_WITH_TESTS:BOOL=ON 55 | ``` 56 | 57 | They can be passed to CMake as `-D=`, e.g. `-DBUILD_SHARED_LIBS=OFF`. 58 | 59 | ## Usage 60 | Basically, there are three data types: messages, devicelists, and bundles. 61 | You can import received the received XML data to work with it, or create them empty. When done with them, they can be exported back to XML for displaying or sending. 62 | 63 | The test cases should demonstrate the general usage. 64 | 65 | Many errors have their own specific error code. This is only for debugging purposes and they are not intended to be stable. 66 | Some error cases which do not have their own error code print a message with more details to `stderr` if the environment variable `LIBOMEMO_DEBUG` is set to any value. 67 | Additionally, `mxml` also prints some errors to `stderr`, which can help with debugging. 68 | 69 | 70 | If a different namespace than the one specified in the XEP is to be used, you can use specify it at compile time. See the makefile for an example. 71 | -------------------------------------------------------------------------------- /libomemo.pc.cmake: -------------------------------------------------------------------------------- 1 | prefix=@_OMEMO_PKGCONF_PREFIX@ 2 | exec_prefix=@_OMEMO_PKGCONF_EXEC_PREFIX@ 3 | libdir=@_OMEMO_PKGCONF_LIBDIR@ 4 | includedir=@_OMEMO_PKGCONF_INCLUDEDIR@ 5 | 6 | Name: libomemo 7 | Version: @PROJECT_VERSION@ 8 | Description: OMEMO library for C 9 | URL: https://github.com/gkdr/libomemo 10 | Requires: glib-2.0 11 | Requires.private: libgcrypt mxml sqlite3 12 | Cflags: -I${includedir}/libomemo 13 | Libs: -L${libdir} -lomemo 14 | -------------------------------------------------------------------------------- /src/libomemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // vsnprintf 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include // b64, glist 9 | 10 | #include 11 | 12 | #include "libomemo.h" 13 | 14 | #define HINTS_XMLNS "urn:xmpp:hints" 15 | 16 | #define OMEMO_NS_SEPARATOR "." 17 | #define OMEMO_NS_SEPARATOR_FINAL ":" 18 | #define OMEMO_NS "eu.siacs.conversations.axolotl" 19 | 20 | #define PEP_NODE_NAME "node" 21 | #define DEVICELIST_PEP_NAME "devicelist" 22 | #define BUNDLE_PEP_NAME "bundles" 23 | 24 | #define OMEMO_DEVICELIST_PEP_NODE OMEMO_NS OMEMO_NS_SEPARATOR DEVICELIST_PEP_NAME 25 | 26 | #define MESSAGE_NODE_NAME "message" 27 | #define BODY_NODE_NAME "body" 28 | #define ENCRYPTED_NODE_NAME "encrypted" 29 | #define HEADER_NODE_NAME "header" 30 | #define IV_NODE_NAME "iv" 31 | #define KEY_NODE_NAME "key" 32 | #define PAYLOAD_NODE_NAME "payload" 33 | #define STORE_NODE_NAME "store" 34 | 35 | #define PUBLISH_NODE_NAME "publish" 36 | #define ITEMS_NODE_NAME "items" 37 | #define ITEM_NODE_NAME "item" 38 | #define LIST_NODE_NAME "list" 39 | #define BUNDLE_NODE_NAME "bundle" 40 | #define SIGNED_PRE_KEY_NODE_NAME "signedPreKeyPublic" 41 | #define SIGNATURE_NODE_NAME "signedPreKeySignature" 42 | #define PREKEYS_NODE_NAME "prekeys" 43 | #define PRE_KEY_NODE_NAME "preKeyPublic" 44 | #define IDENTITY_KEY_NODE_NAME "identityKey" 45 | #define DEVICE_NODE_NAME "device" 46 | 47 | #define XMLNS_ATTR_NAME "xmlns" 48 | #define MESSAGE_NODE_FROM_ATTR_NAME "from" 49 | #define MESSAGE_NODE_TO_ATTR_NAME "to" 50 | #define HEADER_NODE_SID_ATTR_NAME "sid" 51 | #define KEY_NODE_RID_ATTR_NAME "rid" 52 | #define KEY_NODE_PREKEY_ATTR_NAME "prekey" 53 | #define KEY_NODE_PREKEY_ATTR_VAL_TRUE "true" 54 | #define PUBLISH_NODE_NODE_ATTR_NAME "node" 55 | #define SIGNED_PRE_KEY_NODE_ID_ATTR_NAME "signedPreKeyId" 56 | #define PRE_KEY_NODE_ID_ATTR_NAME "preKeyId" 57 | #define DEVICE_NODE_ID_ATTR_NAME "id" 58 | 59 | #define OMEMO_DB_DEFAULT_FN "omemo.sqlite" 60 | 61 | #define NO_OMEMO_MSG "You received an OMEMO encrypted message, but your client does not support it." 62 | 63 | #define EME_NODE_NAME "encryption" 64 | #define EME_XMLNS "urn:xmpp:eme:0" 65 | #define EME_NAMESPACE_ATTR_NAME "namespace" 66 | #define EME_NAME_ATTR_NAME "name" 67 | 68 | struct omemo_bundle { 69 | char * device_id; 70 | mxml_node_t * signed_pk_node_p; 71 | mxml_node_t * signature_node_p; 72 | mxml_node_t * identity_key_node_p; 73 | mxml_node_t * pre_keys_node_p; 74 | size_t pre_keys_amount; 75 | }; 76 | 77 | struct omemo_devicelist { 78 | char * from; 79 | GList * id_list_p; 80 | mxml_node_t * list_node_p; 81 | }; 82 | 83 | struct omemo_message { 84 | mxml_node_t * message_node_p; 85 | mxml_node_t * header_node_p; 86 | mxml_node_t * payload_node_p; 87 | uint8_t * key_p; 88 | size_t key_len; 89 | uint8_t * iv_p; 90 | size_t iv_len; 91 | size_t tag_len; //tag is appended to key buf, i.e. tag_p = key_p + key_len 92 | }; 93 | 94 | /** 95 | * Mostly helps dealing with the device ids that come as an int and have to be a string for XML. 96 | * 97 | * @param in The int to convert. 98 | * @param out Will be set to the number string, needs to be free()d. 99 | * @return Returns the length on success, and negative on error. 100 | */ 101 | static int int_to_string(uint32_t in, char ** out) { 102 | int len; 103 | size_t buf_len; 104 | char * int_string; 105 | 106 | len = snprintf((void *) 0, 0, "%i", in); 107 | if (len < 0) { 108 | return -1; 109 | } 110 | buf_len = len + 1; 111 | int_string = malloc(buf_len); 112 | if (!int_string) { 113 | return OMEMO_ERR_NOMEM; 114 | } 115 | memset(int_string, 0, buf_len); 116 | 117 | int result = snprintf(int_string, buf_len, "%i", in); 118 | if (result != len) { 119 | free(int_string); 120 | return -1; 121 | } 122 | 123 | *out = int_string; 124 | return len; 125 | } 126 | 127 | /** 128 | * Helps basic sanity checking of received XML. 129 | * 130 | * @param node_p Pointer to the current node. 131 | * @param next_node_func The function that returns the next node (e.g. child or sibling) 132 | * @param expected_name The name of the expected node. 133 | * @param next_node_pp Will be set to a pointer to the next node if it is the right one. 134 | * @return 0 on success, negative on error (specifically, OMEMO_ERR_MALFORMED_XML) 135 | */ 136 | static int expect_next_node(mxml_node_t * node_p, mxml_node_t * (*next_node_func)(mxml_node_t * node_p), char * expected_name, mxml_node_t ** next_node_pp) { 137 | mxml_node_t * next_node_p = next_node_func(node_p); 138 | if (!next_node_p) { 139 | return OMEMO_ERR_MALFORMED_XML; 140 | } 141 | 142 | const char * element_name = mxmlGetElement(next_node_p); 143 | if (!element_name) { 144 | return OMEMO_ERR_MALFORMED_XML; 145 | } 146 | 147 | if (strncmp(mxmlGetElement(next_node_p), expected_name, strlen(expected_name))) { 148 | return OMEMO_ERR_MALFORMED_XML; 149 | } 150 | *next_node_pp = next_node_p; 151 | return 0; 152 | } 153 | 154 | #define log_err(format, ...) \ 155 | do { \ 156 | if (getenv("LIBOMEMO_DEBUG")) { \ 157 | fprintf(stderr, "libomemo - error in %s: ", __func__); \ 158 | fprintf(stderr, format, __VA_ARGS__); \ 159 | fprintf(stderr, "\n"); \ 160 | } \ 161 | } while(0) 162 | 163 | int omemo_bundle_create(omemo_bundle ** bundle_pp) { 164 | omemo_bundle * bundle_p = malloc(sizeof(omemo_bundle)); 165 | if (!bundle_p) { 166 | return OMEMO_ERR_NOMEM; 167 | } 168 | memset(bundle_p, 0, sizeof(omemo_bundle)); 169 | 170 | *bundle_pp = bundle_p; 171 | 172 | return 0; 173 | } 174 | 175 | int omemo_bundle_set_device_id(omemo_bundle * bundle_p, uint32_t device_id) { 176 | char * id_string = (void *) 0; 177 | int ret = int_to_string(device_id, &id_string); 178 | if (ret <= 0) { 179 | return ret; 180 | } 181 | 182 | bundle_p->device_id = id_string; 183 | 184 | return OMEMO_SUCCESS; 185 | } 186 | 187 | uint32_t omemo_bundle_get_device_id(omemo_bundle * bundle_p) { 188 | return strtol(bundle_p->device_id, (void *) 0, 0); 189 | } 190 | 191 | int omemo_bundle_set_signed_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id, uint8_t * data_p, size_t data_len) { 192 | int ret_val = 0; 193 | 194 | mxml_node_t * signed_pre_key_node_p = (void *) 0; 195 | char * pre_key_id_string = (void *) 0; 196 | gchar * b64_string = (void *) 0; 197 | 198 | signed_pre_key_node_p = mxmlNewElement(MXML_NO_PARENT, SIGNED_PRE_KEY_NODE_NAME); 199 | if (int_to_string(pre_key_id, &pre_key_id_string) <= 0) { 200 | ret_val = -1; 201 | goto cleanup; 202 | } 203 | mxmlElementSetAttr(signed_pre_key_node_p, SIGNED_PRE_KEY_NODE_ID_ATTR_NAME, pre_key_id_string); 204 | 205 | b64_string = g_base64_encode(data_p, data_len); 206 | (void) mxmlNewOpaque(signed_pre_key_node_p, b64_string); 207 | 208 | bundle_p->signed_pk_node_p = signed_pre_key_node_p; 209 | 210 | cleanup: 211 | if (ret_val < 0) { 212 | mxmlDelete(signed_pre_key_node_p); 213 | } 214 | g_free(b64_string); 215 | free(pre_key_id_string); 216 | 217 | return ret_val; 218 | } 219 | 220 | int omemo_bundle_get_signed_pre_key(omemo_bundle * bundle_p, uint32_t * pre_key_id_p, uint8_t ** data_pp, size_t * data_len_p) { 221 | int ret_val = 0; 222 | 223 | const char * b64_string = (void *) 0; 224 | guchar * data_p = (void *) 0; 225 | gsize len = 0; 226 | const char * pre_key_id_string = (void *) 0; 227 | 228 | if (!bundle_p || !bundle_p->signed_pk_node_p) { 229 | ret_val = OMEMO_ERR_NULL; 230 | goto cleanup; 231 | } 232 | 233 | b64_string = mxmlGetOpaque(bundle_p->signed_pk_node_p); 234 | if (!b64_string) { 235 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_DATA; 236 | goto cleanup; 237 | } 238 | 239 | pre_key_id_string = mxmlElementGetAttr(bundle_p->signed_pk_node_p, SIGNED_PRE_KEY_NODE_ID_ATTR_NAME); 240 | if (!pre_key_id_string) { 241 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_ID_ATTR; 242 | goto cleanup; 243 | } 244 | 245 | data_p = g_base64_decode(b64_string, &len); 246 | 247 | *pre_key_id_p = strtol(pre_key_id_string, (void *) 0, 0); 248 | *data_pp = data_p; 249 | *data_len_p = len; 250 | 251 | cleanup: 252 | 253 | return ret_val; 254 | } 255 | 256 | int omemo_bundle_set_signature(omemo_bundle * bundle_p, uint8_t * data_p, size_t data_len) { 257 | mxml_node_t * signature_node_p = mxmlNewElement(MXML_NO_PARENT, SIGNATURE_NODE_NAME); 258 | 259 | gchar * b64_string = g_base64_encode(data_p, data_len); 260 | (void) mxmlNewOpaque(signature_node_p, b64_string); 261 | 262 | bundle_p->signature_node_p = signature_node_p; 263 | 264 | g_free(b64_string); 265 | 266 | return 0; 267 | } 268 | 269 | int omemo_bundle_get_signature(omemo_bundle * bundle_p, uint8_t ** data_pp, size_t * data_len_p) { 270 | int ret_val = 0; 271 | 272 | const char * b64_string = (void *) 0; 273 | guchar * data_p = (void *) 0; 274 | gsize len = 0; 275 | 276 | if (!bundle_p || !bundle_p->signature_node_p) { 277 | ret_val = OMEMO_ERR_NULL; 278 | goto cleanup; 279 | } 280 | 281 | b64_string = mxmlGetOpaque(bundle_p->signature_node_p); 282 | if (!b64_string) { 283 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_SIG_DATA; 284 | goto cleanup; 285 | } 286 | 287 | data_p = g_base64_decode(b64_string, &len); 288 | 289 | *data_pp = data_p; 290 | *data_len_p = len; 291 | 292 | cleanup: 293 | 294 | return ret_val; 295 | } 296 | 297 | int omemo_bundle_set_identity_key(omemo_bundle * bundle_p, uint8_t * data_p, size_t data_len) { 298 | mxml_node_t * identity_node_p = mxmlNewElement(MXML_NO_PARENT, IDENTITY_KEY_NODE_NAME); 299 | 300 | gchar * b64_string = g_base64_encode(data_p, data_len); 301 | (void) mxmlNewOpaque(identity_node_p, b64_string); 302 | 303 | bundle_p->identity_key_node_p = identity_node_p; 304 | 305 | g_free(b64_string); 306 | 307 | return 0; 308 | } 309 | 310 | int omemo_bundle_get_identity_key(omemo_bundle * bundle_p, uint8_t ** data_pp, size_t * data_len_p) { 311 | int ret_val = 0; 312 | 313 | const char * b64_string = (void *) 0; 314 | guchar * data_p = (void *) 0; 315 | gsize len = 0; 316 | 317 | if (!bundle_p || !bundle_p->identity_key_node_p) { 318 | ret_val = OMEMO_ERR_NULL; 319 | goto cleanup; 320 | } 321 | 322 | b64_string = mxmlGetOpaque(bundle_p->identity_key_node_p); 323 | if (!b64_string) { 324 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_IK_DATA; 325 | goto cleanup; 326 | } 327 | 328 | data_p = g_base64_decode(b64_string, &len); 329 | 330 | *data_pp = data_p; 331 | *data_len_p = len; 332 | 333 | cleanup: 334 | return ret_val; 335 | } 336 | 337 | int omemo_bundle_add_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id, uint8_t * data_p, size_t data_len) { 338 | int ret_val = 0; 339 | 340 | mxml_node_t * prekeys_node_p = (void *) 0; 341 | mxml_node_t * pre_key_node_p = (void *) 0; 342 | char * pre_key_id_string = (void *) 0; 343 | gchar * b64_string = (void *) 0; 344 | 345 | prekeys_node_p = bundle_p->pre_keys_node_p; 346 | if (!prekeys_node_p) { 347 | prekeys_node_p = mxmlNewElement(MXML_NO_PARENT, PREKEYS_NODE_NAME); 348 | } 349 | 350 | pre_key_node_p = mxmlNewElement(MXML_NO_PARENT, PRE_KEY_NODE_NAME); 351 | if (int_to_string(pre_key_id, &pre_key_id_string) <= 0) { 352 | ret_val = -1; 353 | goto cleanup; 354 | } 355 | mxmlElementSetAttr(pre_key_node_p, PRE_KEY_NODE_ID_ATTR_NAME, pre_key_id_string); 356 | 357 | b64_string = g_base64_encode(data_p, data_len); 358 | (void) mxmlNewOpaque(pre_key_node_p, b64_string); 359 | 360 | mxmlAdd(prekeys_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, pre_key_node_p); 361 | 362 | bundle_p->pre_keys_node_p = prekeys_node_p; 363 | bundle_p->pre_keys_amount++; 364 | 365 | cleanup: 366 | if (ret_val < 0) { 367 | mxmlDelete(pre_key_node_p); 368 | } 369 | g_free(b64_string); 370 | free(pre_key_id_string); 371 | 372 | return ret_val; 373 | } 374 | 375 | 376 | int omemo_bundle_get_random_pre_key(omemo_bundle * bundle_p, uint32_t * pre_key_id_p, uint8_t ** data_pp, size_t * data_len_p) { 377 | int ret_val = 0; 378 | 379 | mxml_node_t * pre_key_node_p = (void *) 0; 380 | gint32 random = 0; 381 | mxml_node_t * next_p = (void *) 0; 382 | const char * b64_string = (void *) 0; 383 | guchar * data_p = (void *) 0; 384 | gsize len = 0; 385 | const char * pre_key_id_string = (void *) 0; 386 | 387 | if (!bundle_p || !bundle_p->pre_keys_node_p) { 388 | ret_val = OMEMO_ERR_NULL; 389 | goto cleanup; 390 | } 391 | 392 | ret_val = expect_next_node(bundle_p->pre_keys_node_p, mxmlGetFirstChild, PRE_KEY_NODE_NAME, &pre_key_node_p); 393 | if (ret_val) { 394 | goto cleanup; 395 | } 396 | 397 | random = g_random_int_range(0, bundle_p->pre_keys_amount); 398 | next_p = pre_key_node_p; 399 | for (int i = 0; i < random; i++) { 400 | next_p = mxmlGetNextSibling(next_p); 401 | if (!next_p) { 402 | log_err("failed to forward pointer to desired item %d out of %zu items at index %d", random, bundle_p->pre_keys_amount, i); 403 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE; 404 | goto cleanup; 405 | } 406 | } 407 | 408 | pre_key_id_string = mxmlElementGetAttr(next_p, PRE_KEY_NODE_ID_ATTR_NAME); 409 | if (!pre_key_id_string) { 410 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_ID_ATTR; 411 | goto cleanup; 412 | } 413 | 414 | b64_string = mxmlGetOpaque(next_p); 415 | if (!b64_string) { 416 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_DATA; 417 | goto cleanup; 418 | } 419 | 420 | data_p = g_base64_decode(b64_string, &len); 421 | 422 | *pre_key_id_p = strtol(pre_key_id_string, (void *) 0, 0); 423 | *data_pp = data_p; 424 | *data_len_p = len; 425 | 426 | cleanup: 427 | 428 | return ret_val; 429 | } 430 | 431 | int omemo_bundle_export(omemo_bundle * bundle_p, char ** publish) { 432 | int ret_val = 0; 433 | 434 | char * node_value = (void *) 0; 435 | const char * format = "%s%s%s%s%s"; 436 | size_t len = 0; 437 | mxml_node_t * publish_node_p = (void *) 0; 438 | mxml_node_t * item_node_p = (void *) 0; 439 | mxml_node_t * bundle_node_p = (void *) 0; 440 | char * out = (void *) 0; 441 | 442 | if (!bundle_p->device_id || !bundle_p->signed_pk_node_p || !bundle_p->signature_node_p || !bundle_p->identity_key_node_p || !bundle_p->pre_keys_node_p) { 443 | ret_val = -1; 444 | goto cleanup; 445 | } 446 | if (bundle_p->pre_keys_amount < 20) { 447 | ret_val = -2; 448 | goto cleanup; 449 | } 450 | if (bundle_p->pre_keys_amount < 100) { 451 | //TODO: maybe issue warning 452 | //FIXME: numbers into constants 453 | } 454 | 455 | len = snprintf((void *) 0, 0, format, OMEMO_NS, OMEMO_NS_SEPARATOR, BUNDLE_PEP_NAME, OMEMO_NS_SEPARATOR_FINAL, bundle_p->device_id) + 1; 456 | node_value = malloc(len); 457 | if (snprintf(node_value, len, format, OMEMO_NS, OMEMO_NS_SEPARATOR, BUNDLE_PEP_NAME, OMEMO_NS_SEPARATOR_FINAL, bundle_p->device_id) <= 0) { 458 | ret_val = -4; 459 | goto cleanup; 460 | } 461 | 462 | publish_node_p = mxmlNewElement(MXML_NO_PARENT, PUBLISH_NODE_NAME); 463 | mxmlElementSetAttr(publish_node_p, PUBLISH_NODE_NODE_ATTR_NAME, node_value); 464 | 465 | item_node_p = mxmlNewElement(publish_node_p, ITEM_NODE_NAME); 466 | 467 | bundle_node_p = mxmlNewElement(item_node_p, BUNDLE_NODE_NAME); 468 | mxmlElementSetAttr(bundle_node_p, "xmlns", OMEMO_NS); 469 | 470 | mxmlAdd(bundle_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, bundle_p->signed_pk_node_p); 471 | mxmlAdd(bundle_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, bundle_p->signature_node_p); 472 | mxmlAdd(bundle_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, bundle_p->identity_key_node_p); 473 | mxmlAdd(bundle_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, bundle_p->pre_keys_node_p); 474 | 475 | out = mxmlSaveAllocString(publish_node_p, MXML_NO_CALLBACK); 476 | if (!out) { 477 | ret_val = -5; 478 | goto cleanup; 479 | } 480 | 481 | *publish = out; 482 | 483 | cleanup: 484 | free(node_value); 485 | 486 | return ret_val; 487 | } 488 | 489 | int omemo_bundle_import (const char * received_bundle, omemo_bundle ** bundle_pp) { 490 | int ret_val = 0; 491 | 492 | omemo_bundle * bundle_p = (void *) 0; 493 | mxml_node_t * items_node_p = (void *) 0; 494 | mxml_node_t * item_node_p = (void *) 0; 495 | mxml_node_t * bundle_node_p = (void *) 0; 496 | const char * bundle_node_name = (void *) 0; 497 | char ** split = (void *) 0; 498 | char * device_id = (void *) 0; 499 | mxml_node_t * signed_pk_node_p = (void *) 0; 500 | mxml_node_t * signature_node_p = (void *) 0; 501 | mxml_node_t * identity_key_node_p = (void *) 0; 502 | mxml_node_t * prekeys_node_p = (void *) 0; 503 | mxml_node_t * pre_key_node_p = (void *) 0; 504 | size_t pre_keys_count = 0; 505 | 506 | ret_val = omemo_bundle_create(&bundle_p); 507 | if (ret_val) { 508 | goto cleanup; 509 | } 510 | 511 | items_node_p = mxmlLoadString((void *) 0, received_bundle, MXML_OPAQUE_CALLBACK); 512 | if (!items_node_p) { 513 | log_err("received bundle response is invalid XML: %s", received_bundle); 514 | ret_val = OMEMO_ERR_MALFORMED_XML; 515 | goto cleanup; 516 | } 517 | 518 | ret_val = strncmp(mxmlGetElement(items_node_p), ITEMS_NODE_NAME, strlen(ITEMS_NODE_NAME)); 519 | if (ret_val) { 520 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_ITEMS_ELEM; 521 | goto cleanup; 522 | } 523 | 524 | bundle_node_name = mxmlElementGetAttr(items_node_p, PEP_NODE_NAME); 525 | if (!bundle_node_name) { 526 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_NODE_ATTR; 527 | goto cleanup; 528 | } 529 | 530 | split = g_strsplit(bundle_node_name, OMEMO_NS_SEPARATOR_FINAL, 6); 531 | if (!g_strcmp0(OMEMO_NS_SEPARATOR, OMEMO_NS_SEPARATOR_FINAL)) { 532 | device_id = g_strdup(split[5]); 533 | } else { 534 | device_id = g_strdup(split[1]); 535 | } 536 | bundle_p->device_id = device_id; 537 | 538 | item_node_p = mxmlFindPath(items_node_p, ITEM_NODE_NAME); 539 | if (!item_node_p) { 540 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_ITEM_ELEM; 541 | goto cleanup; 542 | } 543 | 544 | bundle_node_p = mxmlFindPath(item_node_p, BUNDLE_NODE_NAME); 545 | if (!bundle_node_p) { 546 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_BUNDLE_ELEM; 547 | goto cleanup; 548 | } 549 | 550 | signed_pk_node_p = mxmlFindPath(bundle_node_p, SIGNED_PRE_KEY_NODE_NAME); 551 | if (!signed_pk_node_p) { 552 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_ELEM; 553 | goto cleanup; 554 | } 555 | signed_pk_node_p = mxmlGetParent(signed_pk_node_p); 556 | bundle_p->signed_pk_node_p = signed_pk_node_p; 557 | 558 | signature_node_p = mxmlFindPath(bundle_node_p, SIGNATURE_NODE_NAME); 559 | if (!signature_node_p) { 560 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_SIG_ELEM; 561 | goto cleanup; 562 | } 563 | signature_node_p = mxmlGetParent(signature_node_p); 564 | bundle_p->signature_node_p = signature_node_p; 565 | 566 | identity_key_node_p = mxmlFindPath(bundle_node_p, IDENTITY_KEY_NODE_NAME); 567 | if (!identity_key_node_p) { 568 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_IK_ELEM; 569 | goto cleanup; 570 | } 571 | identity_key_node_p = mxmlGetParent(identity_key_node_p); 572 | bundle_p->identity_key_node_p = identity_key_node_p; 573 | 574 | prekeys_node_p = mxmlFindPath(bundle_node_p, PREKEYS_NODE_NAME); 575 | if (!prekeys_node_p) { 576 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEYS_ELEM; 577 | goto cleanup; 578 | } 579 | bundle_p->pre_keys_node_p = prekeys_node_p; 580 | 581 | pre_key_node_p = mxmlFindPath(prekeys_node_p, PRE_KEY_NODE_NAME); 582 | if (!pre_key_node_p) { 583 | ret_val = OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_ELEM; 584 | goto cleanup; 585 | } 586 | pre_key_node_p = mxmlGetParent(pre_key_node_p); 587 | pre_keys_count++; 588 | pre_key_node_p = mxmlGetNextSibling(pre_key_node_p); 589 | 590 | while (pre_key_node_p) { 591 | pre_keys_count++; 592 | pre_key_node_p = mxmlGetNextSibling(pre_key_node_p); 593 | } 594 | bundle_p->pre_keys_amount = pre_keys_count; 595 | 596 | mxmlRemove(signed_pk_node_p); 597 | mxmlRemove(signature_node_p); 598 | mxmlRemove(identity_key_node_p); 599 | mxmlRemove(prekeys_node_p); 600 | 601 | *bundle_pp = bundle_p; 602 | 603 | cleanup: 604 | if (ret_val) { 605 | omemo_bundle_destroy(bundle_p); 606 | } 607 | mxmlDelete(items_node_p); 608 | g_strfreev(split); 609 | 610 | return ret_val; 611 | } 612 | 613 | int omemo_bundle_get_pep_node_name(uint32_t device_id, char ** node_name_p) { 614 | const char * format = "%s%s%s%s%i"; 615 | size_t len = snprintf((void *) 0, 0, format, OMEMO_NS, OMEMO_NS_SEPARATOR, BUNDLE_PEP_NAME, OMEMO_NS_SEPARATOR_FINAL, device_id); 616 | size_t buf_len = len + 1; 617 | 618 | char * node_name = malloc(buf_len); 619 | if (!node_name) { 620 | return -1; 621 | } 622 | 623 | size_t actual_len = snprintf(node_name, buf_len, format, OMEMO_NS, OMEMO_NS_SEPARATOR, BUNDLE_PEP_NAME, OMEMO_NS_SEPARATOR_FINAL, device_id); 624 | if (actual_len != len) { 625 | free(node_name); 626 | return -2; 627 | } 628 | 629 | *node_name_p = node_name; 630 | return 0; 631 | } 632 | 633 | void omemo_bundle_destroy(omemo_bundle * bundle_p) { 634 | if (bundle_p) { 635 | mxmlDelete(bundle_p->signed_pk_node_p); 636 | mxmlDelete(bundle_p->signature_node_p); 637 | mxmlDelete(bundle_p->identity_key_node_p); 638 | mxmlDelete(bundle_p->pre_keys_node_p); 639 | free(bundle_p->device_id); 640 | free(bundle_p); 641 | } 642 | } 643 | 644 | int omemo_devicelist_create(const char * from, omemo_devicelist ** dl_pp) { 645 | if (!from || !dl_pp) { 646 | return OMEMO_ERR_NULL; 647 | } 648 | 649 | int ret_val = 0; 650 | 651 | omemo_devicelist * dl_p = (void *) 0; 652 | char * from_dup = (void *) 0; 653 | mxml_node_t * list_node_p = (void *) 0; 654 | 655 | dl_p = malloc(sizeof(omemo_devicelist)); 656 | if (!dl_p) { 657 | ret_val = OMEMO_ERR_NOMEM; 658 | goto cleanup; 659 | } 660 | 661 | from_dup = g_strndup(from, strlen(from)); 662 | if (!from_dup) { 663 | ret_val = OMEMO_ERR_NOMEM; 664 | goto cleanup; 665 | } 666 | 667 | list_node_p = mxmlNewElement(MXML_NO_PARENT, LIST_NODE_NAME); 668 | mxmlElementSetAttr(list_node_p, XMLNS_ATTR_NAME, OMEMO_NS); 669 | 670 | dl_p->list_node_p = list_node_p; 671 | dl_p->id_list_p = (void *) 0; 672 | dl_p->from = from_dup; 673 | *dl_pp = dl_p; 674 | 675 | cleanup: 676 | if (ret_val) { 677 | free(from_dup); 678 | free(dl_p); 679 | } 680 | return ret_val; 681 | } 682 | 683 | int omemo_devicelist_import(char * received_devicelist, const char * from, omemo_devicelist ** dl_pp) { 684 | if (!received_devicelist || !from || !dl_pp) { 685 | return OMEMO_ERR_NULL; 686 | } 687 | 688 | int ret_val = 0; 689 | 690 | omemo_devicelist * dl_p = (void *) 0; 691 | mxml_node_t * items_node_p = (void *) 0; 692 | mxml_node_t * item_node_p = (void *) 0; 693 | mxml_node_t * list_node_p = (void *) 0; 694 | mxml_node_t * device_node_p = (void *) 0; 695 | GList * id_list_p = (void *) 0; 696 | 697 | ret_val = omemo_devicelist_create(from, &dl_p); 698 | if (ret_val) { 699 | goto cleanup; 700 | } 701 | 702 | items_node_p = mxmlLoadString((void *) 0, received_devicelist, MXML_NO_CALLBACK); 703 | if (!items_node_p) { 704 | log_err("received devicelist response is invalid XML: %s", received_devicelist); 705 | ret_val = OMEMO_ERR_MALFORMED_XML; 706 | goto cleanup; 707 | } 708 | 709 | ret_val = strncmp(mxmlGetElement(items_node_p), ITEMS_NODE_NAME, strlen(ITEMS_NODE_NAME)); 710 | if (ret_val) { 711 | ret_val = OMEMO_ERR_MALFORMED_DEVICELIST_NO_ITEMS_ELEM; 712 | goto cleanup; 713 | } 714 | 715 | item_node_p = mxmlGetFirstChild(items_node_p); 716 | if (!item_node_p) { 717 | // valid empty response; no child elements at all 718 | ret_val = 0; 719 | *dl_pp = dl_p; 720 | goto cleanup; 721 | } 722 | 723 | ret_val = strncmp(mxmlGetElement(item_node_p), ITEM_NODE_NAME, strlen(ITEM_NODE_NAME)); 724 | if (ret_val) { 725 | // child of element is not an element 726 | ret_val = OMEMO_ERR_MALFORMED_DEVICELIST_NO_ITEM_ELEM; 727 | goto cleanup; 728 | } 729 | 730 | ret_val = expect_next_node(item_node_p, mxmlGetFirstChild, LIST_NODE_NAME, &list_node_p); 731 | if (ret_val) { 732 | ret_val = OMEMO_ERR_MALFORMED_DEVICELIST_NO_LIST_ELEM; 733 | goto cleanup; 734 | } 735 | 736 | mxmlDelete(dl_p->list_node_p); 737 | mxmlRemove(list_node_p); 738 | dl_p->list_node_p = list_node_p; 739 | 740 | ret_val = expect_next_node(list_node_p, mxmlGetFirstChild, DEVICE_NODE_NAME, &device_node_p); 741 | if (ret_val) { 742 | // another variant of valid empty list: a element inside an element, buz with no children 743 | ret_val = 0; 744 | *dl_pp = dl_p; 745 | goto cleanup; 746 | } 747 | 748 | size_t device_count = 0; 749 | while (device_node_p) { 750 | device_count++; 751 | 752 | const char * id_string = mxmlElementGetAttr(device_node_p, DEVICE_NODE_ID_ATTR_NAME); 753 | if (!id_string) { 754 | log_err("device element #%zu does not have an ID attribute", device_count); 755 | ret_val = OMEMO_ERR_MALFORMED_DEVICELIST_NO_DEVICE_ID_ATTR; 756 | goto cleanup; 757 | } 758 | 759 | uint32_t * id_temp_p = malloc(sizeof(uint32_t)); 760 | if (!id_temp_p) { 761 | ret_val = OMEMO_ERR_NOMEM; 762 | goto cleanup; 763 | } 764 | 765 | *id_temp_p = strtol(id_string, (void *) 0, 0); 766 | id_list_p = g_list_append(id_list_p, id_temp_p); 767 | 768 | device_node_p = mxmlGetNextSibling(device_node_p); 769 | } 770 | dl_p->id_list_p = id_list_p; 771 | 772 | *dl_pp = dl_p; 773 | 774 | cleanup: 775 | if (ret_val) { 776 | omemo_devicelist_destroy(dl_p); 777 | g_list_free_full(id_list_p, free); 778 | } 779 | mxmlDelete(items_node_p); 780 | return ret_val; 781 | } 782 | 783 | int omemo_devicelist_add(omemo_devicelist * dl_p, uint32_t device_id) { 784 | if (!dl_p || !dl_p->list_node_p) { 785 | return OMEMO_ERR_NULL; 786 | } 787 | 788 | uint32_t * id_p = malloc(sizeof(uint32_t)); 789 | if (!id_p) { 790 | return OMEMO_ERR_NOMEM; 791 | } 792 | *id_p = device_id; 793 | 794 | char * id_string; 795 | int id_string_len = int_to_string(device_id, &id_string); 796 | if (id_string_len < 1) { 797 | free(id_p); 798 | return OMEMO_ERR; 799 | } 800 | 801 | mxml_node_t * device_node_p = mxmlNewElement(MXML_NO_PARENT, DEVICE_NODE_NAME); 802 | mxmlElementSetAttr(device_node_p, DEVICE_NODE_ID_ATTR_NAME, id_string); 803 | mxmlAdd(dl_p->list_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, device_node_p); 804 | dl_p->id_list_p = g_list_append(dl_p->id_list_p, id_p); 805 | 806 | return 0; 807 | } 808 | 809 | int omemo_devicelist_contains_id(const omemo_devicelist * dl_p, uint32_t device_id) { 810 | if (!dl_p || !dl_p->list_node_p) { 811 | return 0; 812 | } 813 | 814 | GList * curr = dl_p->id_list_p; 815 | while(curr) { 816 | if (*((uint32_t *) curr->data) == device_id) { 817 | return 1; 818 | } 819 | 820 | curr = curr->next; 821 | } 822 | 823 | return 0; 824 | } 825 | 826 | int omemo_devicelist_remove(omemo_devicelist * dl_p, uint32_t device_id) { 827 | if (!dl_p) { 828 | return OMEMO_ERR_NULL; 829 | } 830 | int ret_val = 0; 831 | 832 | char * device_id_str = (void *) 0; 833 | mxml_node_t * device_node_p = (void *) 0; 834 | GList * curr_p = (void *) 0; 835 | uint32_t * remove_id_p = (void *) 0; 836 | 837 | ret_val = int_to_string(device_id, &device_id_str); 838 | if (ret_val < 1) { 839 | ret_val = OMEMO_ERR; 840 | goto cleanup; 841 | } 842 | ret_val = 0; 843 | 844 | device_node_p = mxmlFindElement(dl_p->list_node_p, dl_p->list_node_p, DEVICE_NODE_NAME, DEVICE_NODE_ID_ATTR_NAME, device_id_str, MXML_DESCEND); 845 | if (!device_node_p) { 846 | goto cleanup; 847 | } 848 | mxmlDelete(device_node_p); 849 | 850 | for (curr_p = dl_p->id_list_p; curr_p; curr_p = curr_p->next) { 851 | if (omemo_devicelist_list_data(curr_p) == device_id) { 852 | remove_id_p = curr_p->data; 853 | break; 854 | } 855 | } 856 | 857 | dl_p->id_list_p = g_list_remove(dl_p->id_list_p, remove_id_p); 858 | 859 | cleanup: 860 | free(device_id_str); 861 | return ret_val; 862 | } 863 | 864 | int omemo_devicelist_is_empty(omemo_devicelist * dl_p) { 865 | return dl_p->id_list_p ? 0 : 1; 866 | } 867 | 868 | int omemo_devicelist_diff(const omemo_devicelist * dl_a_p, const omemo_devicelist * dl_b_p, GList ** a_minus_b_pp, GList ** b_minus_a_pp) { 869 | if (!dl_a_p || !dl_b_p || !a_minus_b_pp || !b_minus_a_pp) { 870 | return OMEMO_ERR_NULL; 871 | } 872 | 873 | GList * a_l_p = (void *) 0; 874 | GList * b_l_p = (void *) 0; 875 | GList * amb_p = (void *) 0; 876 | GList * bma_p = (void *) 0; 877 | GList * curr_p = (void *) 0; 878 | GList * temp_p = (void *) 0; 879 | 880 | a_l_p = omemo_devicelist_get_id_list(dl_a_p); 881 | b_l_p = omemo_devicelist_get_id_list(dl_b_p); 882 | 883 | curr_p = a_l_p; 884 | while (curr_p) { 885 | temp_p = curr_p; 886 | curr_p = curr_p->next; 887 | if (!omemo_devicelist_contains_id(dl_b_p, omemo_devicelist_list_data(temp_p))) { 888 | a_l_p = g_list_remove_link(a_l_p, temp_p); 889 | amb_p = g_list_prepend(amb_p, temp_p->data); 890 | g_list_free_1(temp_p); 891 | } 892 | } 893 | 894 | curr_p = b_l_p; 895 | while (curr_p) { 896 | temp_p = curr_p; 897 | curr_p = curr_p->next; 898 | if (!omemo_devicelist_contains_id(dl_a_p, omemo_devicelist_list_data(temp_p))) { 899 | b_l_p = g_list_remove_link(b_l_p, temp_p); 900 | bma_p = g_list_prepend(bma_p, temp_p->data); 901 | g_list_free_1(temp_p); 902 | } 903 | } 904 | 905 | *a_minus_b_pp = amb_p; 906 | *b_minus_a_pp = bma_p; 907 | 908 | g_list_free_full(a_l_p, free); 909 | g_list_free_full(b_l_p, free); 910 | 911 | return 0; 912 | } 913 | 914 | int omemo_devicelist_export(omemo_devicelist * dl_p, char ** xml_p) { 915 | if (!dl_p || !dl_p->list_node_p || !xml_p) { 916 | return OMEMO_ERR_NULL; 917 | } 918 | 919 | mxml_node_t * publish_node_p = mxmlNewElement(MXML_NO_PARENT, PUBLISH_NODE_NAME); 920 | mxmlElementSetAttr(publish_node_p, PUBLISH_NODE_NODE_ATTR_NAME, OMEMO_DEVICELIST_PEP_NODE); 921 | 922 | mxml_node_t * item_node_p = mxmlNewElement(publish_node_p, ITEM_NODE_NAME); 923 | mxmlAdd(item_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, dl_p->list_node_p); 924 | 925 | char * xml = mxmlSaveAllocString(publish_node_p, MXML_NO_CALLBACK); 926 | if (!xml) { 927 | return OMEMO_ERR; 928 | } 929 | 930 | *xml_p = xml; 931 | return 0; 932 | } 933 | 934 | GList * omemo_devicelist_get_id_list(const omemo_devicelist * dl_p) { 935 | GList * new_l_p = (void *) 0; 936 | GList * curr_p = (void *) 0; 937 | uint32_t * cpy_p = (void *) 0; 938 | 939 | for (curr_p = dl_p->id_list_p; curr_p; curr_p = curr_p->next) { 940 | cpy_p = malloc(sizeof(uint32_t)); 941 | if (!cpy_p) { 942 | g_list_free_full(new_l_p, free); 943 | return (void *) 0; 944 | } 945 | memcpy(cpy_p, curr_p->data, (sizeof(uint32_t))); 946 | 947 | new_l_p = g_list_append(new_l_p, cpy_p); 948 | } 949 | 950 | return new_l_p; 951 | } 952 | 953 | int omemo_devicelist_has_id_list(const omemo_devicelist * dl_p) { 954 | return (dl_p->id_list_p) ? 1 : 0; 955 | } 956 | 957 | const char * omemo_devicelist_get_owner(const omemo_devicelist * dl_p) { 958 | return dl_p ? dl_p->from : (void *) 0; 959 | } 960 | 961 | int omemo_devicelist_get_pep_node_name(char ** node_name_p) { 962 | char * format = "%s%s%s"; 963 | size_t len = snprintf((void *) 0, 0, format, OMEMO_NS, OMEMO_NS_SEPARATOR, DEVICELIST_PEP_NAME); 964 | size_t buf_len = len + 1; 965 | 966 | char * node_name = malloc(buf_len); 967 | if (!node_name) { 968 | return -1; 969 | } 970 | 971 | size_t actual_len = snprintf(node_name, buf_len, format, OMEMO_NS, OMEMO_NS_SEPARATOR, DEVICELIST_PEP_NAME); 972 | if (actual_len != len) { 973 | free(node_name); 974 | return -2; 975 | } 976 | 977 | *node_name_p = node_name; 978 | return 0; 979 | } 980 | 981 | void omemo_devicelist_destroy(omemo_devicelist * dl_p) { 982 | if (dl_p) { 983 | g_list_free_full(dl_p->id_list_p, free); 984 | mxmlDelete(dl_p->list_node_p); 985 | free(dl_p->from); 986 | free(dl_p); 987 | } 988 | } 989 | 990 | int omemo_message_create(uint32_t sender_device_id, const omemo_crypto_provider * crypto_p, omemo_message ** message_pp) { 991 | if (!crypto_p || !crypto_p->random_bytes_func || !crypto_p->aes_gcm_encrypt_func || !message_pp) { 992 | return OMEMO_ERR_NULL; 993 | } 994 | 995 | int ret_val = 0; 996 | 997 | omemo_message * msg_p = (void *) 0; 998 | uint8_t * iv_p = (void *) 0; 999 | gchar * iv_b64 = (void *) 0; 1000 | char * device_id_string = (void *) 0; 1001 | mxml_node_t * header_node_p = (void *) 0; 1002 | mxml_node_t * iv_node_p = (void *) 0; 1003 | uint8_t * key_p = (void *) 0; 1004 | 1005 | msg_p = malloc(sizeof(omemo_message)); 1006 | if (!msg_p) { 1007 | ret_val = OMEMO_ERR_NOMEM; 1008 | goto cleanup; 1009 | } 1010 | memset(msg_p, 0, sizeof(omemo_message)); 1011 | 1012 | ret_val = crypto_p->random_bytes_func(&iv_p, OMEMO_AES_GCM_IV_LENGTH, crypto_p->user_data_p); 1013 | if (ret_val) { 1014 | goto cleanup; 1015 | } 1016 | msg_p->iv_p = iv_p; 1017 | msg_p->iv_len = OMEMO_AES_GCM_IV_LENGTH; 1018 | iv_b64 = g_base64_encode(iv_p, OMEMO_AES_GCM_IV_LENGTH); 1019 | 1020 | if (int_to_string(sender_device_id, &device_id_string) <= 0) { 1021 | ret_val = -1; 1022 | goto cleanup; 1023 | } 1024 | header_node_p = mxmlNewElement(MXML_NO_PARENT, HEADER_NODE_NAME); 1025 | mxmlElementSetAttr(header_node_p, HEADER_NODE_SID_ATTR_NAME, device_id_string); 1026 | 1027 | iv_node_p = mxmlNewElement(header_node_p, IV_NODE_NAME); 1028 | (void) mxmlNewOpaque(iv_node_p, iv_b64); 1029 | msg_p->header_node_p = header_node_p; 1030 | 1031 | ret_val = crypto_p->random_bytes_func(&key_p, OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH, crypto_p->user_data_p); 1032 | if (ret_val) { 1033 | goto cleanup; 1034 | } 1035 | 1036 | msg_p->key_p = key_p; 1037 | msg_p->key_len = OMEMO_AES_128_KEY_LENGTH; 1038 | msg_p->tag_len = 0; 1039 | 1040 | *message_pp = msg_p; 1041 | 1042 | cleanup: 1043 | if (ret_val) { 1044 | omemo_message_destroy(msg_p); 1045 | } 1046 | 1047 | free(device_id_string); 1048 | g_free(iv_b64); 1049 | 1050 | return ret_val; 1051 | } 1052 | 1053 | int omemo_message_strip_possible_plaintext(omemo_message * msg_p) { 1054 | if (!msg_p) { 1055 | return OMEMO_ERR_NULL; 1056 | } 1057 | 1058 | int ret_val = 0; 1059 | mxml_node_t * bad_node_p = (void *) 0; 1060 | 1061 | bad_node_p = mxmlFindElement(msg_p->message_node_p, msg_p->message_node_p, "html", NULL, NULL, MXML_DESCEND_FIRST); 1062 | if (bad_node_p) { 1063 | mxmlDelete(bad_node_p); 1064 | } 1065 | 1066 | for (bad_node_p = mxmlFindElement(msg_p->message_node_p, msg_p->message_node_p, "body", NULL, NULL, MXML_DESCEND_FIRST); 1067 | bad_node_p; 1068 | bad_node_p = mxmlFindElement(msg_p->message_node_p, msg_p->message_node_p, "body", NULL, NULL, MXML_DESCEND_FIRST)) { 1069 | mxmlDelete(bad_node_p); 1070 | } 1071 | 1072 | return ret_val; 1073 | } 1074 | 1075 | int omemo_message_prepare_encryption(char * outgoing_message, uint32_t sender_device_id, const omemo_crypto_provider * crypto_p, int strip, omemo_message ** message_pp) { 1076 | if (!outgoing_message || !crypto_p || !crypto_p->random_bytes_func || !crypto_p->aes_gcm_encrypt_func || !message_pp) { 1077 | return OMEMO_ERR_NULL; 1078 | } 1079 | 1080 | int ret_val = 0; 1081 | 1082 | omemo_message * msg_p = (void *) 0; 1083 | mxml_node_t * msg_node_p = (void *) 0; 1084 | mxml_node_t * body_node_p = (void *) 0; 1085 | const char * msg_text = (void *) 0; 1086 | 1087 | uint8_t * ct_p = (void *) 0; 1088 | size_t ct_len = 0; 1089 | 1090 | gchar * payload_b64 = (void *) 0; 1091 | mxml_node_t * payload_node_p = (void *) 0; 1092 | 1093 | uint8_t * tag_p = (void *) 0; 1094 | 1095 | ret_val = omemo_message_create(sender_device_id, crypto_p, &msg_p); 1096 | if (ret_val) { 1097 | goto cleanup; 1098 | } 1099 | 1100 | msg_node_p = mxmlLoadString((void *) 0, outgoing_message, MXML_OPAQUE_CALLBACK); 1101 | if (!msg_node_p) { 1102 | log_err("outgoing message is invalid XML: %s", outgoing_message); 1103 | ret_val = OMEMO_ERR_MALFORMED_XML; 1104 | goto cleanup; 1105 | } 1106 | msg_p->message_node_p = msg_node_p; 1107 | 1108 | body_node_p = mxmlFindPath(msg_node_p, BODY_NODE_NAME); 1109 | if (!body_node_p) { 1110 | ret_val = OMEMO_ERR_MALFORMED_OUTGOING_MESSAGE_NO_BODY_ELEM; 1111 | goto cleanup; 1112 | } 1113 | 1114 | msg_text = mxmlGetOpaque(body_node_p); 1115 | if (!msg_text) { 1116 | ret_val = OMEMO_ERR_MALFORMED_OUTGOING_MESSAGE_NO_BODY_DATA; 1117 | goto cleanup; 1118 | } 1119 | 1120 | ret_val = crypto_p->aes_gcm_encrypt_func((uint8_t *) msg_text, strlen(msg_text), 1121 | msg_p->iv_p, msg_p->iv_len, 1122 | msg_p->key_p, msg_p->key_len, 1123 | OMEMO_AES_GCM_TAG_LENGTH, 1124 | crypto_p->user_data_p, 1125 | &ct_p, &ct_len, 1126 | &tag_p); 1127 | if (ret_val) { 1128 | goto cleanup; 1129 | } 1130 | 1131 | msg_p->tag_len = OMEMO_AES_GCM_TAG_LENGTH; 1132 | memcpy(msg_p->key_p + msg_p->key_len, tag_p, msg_p->tag_len); 1133 | 1134 | // to delete it, go one level up 1135 | ret_val = expect_next_node(body_node_p, mxmlGetParent, BODY_NODE_NAME, &body_node_p); 1136 | if (ret_val) { 1137 | log_err("failed to navigate to %s node for deletion", BODY_NODE_NAME); 1138 | ret_val = OMEMO_ERR_MALFORMED_OUTGOING_MESSAGE_NO_BODY_ELEM; 1139 | goto cleanup; 1140 | } 1141 | 1142 | mxmlRemove(body_node_p); 1143 | 1144 | payload_b64 = g_base64_encode(ct_p, ct_len); 1145 | payload_node_p = mxmlNewElement(MXML_NO_PARENT, PAYLOAD_NODE_NAME); 1146 | (void) mxmlNewOpaque(payload_node_p, payload_b64); 1147 | msg_p->payload_node_p = payload_node_p; 1148 | 1149 | if (strip == OMEMO_STRIP_ALL) { 1150 | omemo_message_strip_possible_plaintext(msg_p); 1151 | } 1152 | 1153 | *message_pp = msg_p; 1154 | 1155 | cleanup: 1156 | if (ret_val) { 1157 | omemo_message_destroy(msg_p); 1158 | } 1159 | 1160 | free(ct_p); 1161 | g_free(payload_b64); 1162 | free(tag_p); 1163 | 1164 | return ret_val; 1165 | } 1166 | 1167 | const uint8_t * omemo_message_get_key(omemo_message * msg_p) { 1168 | return (msg_p) ? msg_p->key_p : (void *) 0; 1169 | } 1170 | 1171 | size_t omemo_message_get_key_len(omemo_message * msg_p) { 1172 | return msg_p->key_len + msg_p->tag_len; 1173 | } 1174 | 1175 | // adds a "key" element with the given parameters to the header. 1176 | static int add_recipient(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len, bool prekey) { 1177 | if (!msg_p || !msg_p->header_node_p || !encrypted_key_p) { 1178 | return OMEMO_ERR_NULL; 1179 | } 1180 | 1181 | char * device_id_string = (void *) 0; 1182 | if (int_to_string(device_id, &device_id_string) <= 0) { 1183 | return OMEMO_ERR; 1184 | } 1185 | 1186 | gchar * key_b64 = g_base64_encode(encrypted_key_p, key_len); 1187 | mxml_node_t * key_node_p = mxmlNewElement(MXML_NO_PARENT, KEY_NODE_NAME); 1188 | mxmlElementSetAttr(key_node_p, KEY_NODE_RID_ATTR_NAME, device_id_string); 1189 | (void) mxmlNewOpaque(key_node_p, key_b64); 1190 | 1191 | if (prekey) { 1192 | mxmlElementSetAttr(key_node_p, KEY_NODE_PREKEY_ATTR_NAME, KEY_NODE_PREKEY_ATTR_VAL_TRUE); 1193 | } 1194 | 1195 | mxmlAdd(msg_p->header_node_p, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT, key_node_p); 1196 | 1197 | free(device_id_string); 1198 | g_free(key_b64); 1199 | return 0; 1200 | } 1201 | 1202 | int omemo_message_add_recipient(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len) { 1203 | return add_recipient(msg_p, device_id, encrypted_key_p, key_len, false); 1204 | } 1205 | 1206 | int omemo_message_add_recipient_w_prekey(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len) { 1207 | return add_recipient(msg_p, device_id, encrypted_key_p, key_len, true); 1208 | } 1209 | 1210 | int omemo_message_export_encrypted(omemo_message * msg_p, int add_msg, char ** msg_xml) { 1211 | if (!msg_p || !msg_p->message_node_p || !msg_p->header_node_p || !msg_p->payload_node_p || !msg_xml) { 1212 | return OMEMO_ERR_NULL; 1213 | } 1214 | 1215 | int ret_val = 0; 1216 | 1217 | mxml_node_t * body_node_p = (void *) 0; 1218 | mxml_node_t * encrypted_node_p = (void *) 0; 1219 | mxml_node_t * eme_node_p = (void *) 0; 1220 | mxml_node_t * store_node_p = (void *) 0; 1221 | char * xml_str = (void *) 0; 1222 | 1223 | if (add_msg == OMEMO_ADD_MSG_BODY || add_msg == OMEMO_ADD_MSG_BOTH) { 1224 | body_node_p = mxmlNewElement(msg_p->message_node_p, BODY_NODE_NAME); 1225 | (void) mxmlNewOpaque(body_node_p, NO_OMEMO_MSG); 1226 | } 1227 | 1228 | encrypted_node_p = mxmlNewElement(msg_p->message_node_p, ENCRYPTED_NODE_NAME); 1229 | mxmlElementSetAttr(encrypted_node_p, XMLNS_ATTR_NAME, OMEMO_NS); 1230 | 1231 | mxmlAdd(encrypted_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, msg_p->header_node_p); 1232 | mxmlAdd(encrypted_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, msg_p->payload_node_p); 1233 | 1234 | if (add_msg == OMEMO_ADD_MSG_EME || add_msg == OMEMO_ADD_MSG_BOTH) { 1235 | eme_node_p = mxmlNewElement(msg_p->message_node_p, EME_NODE_NAME); 1236 | mxmlElementSetAttr(eme_node_p, XMLNS_ATTR_NAME, EME_XMLNS); 1237 | mxmlElementSetAttr(eme_node_p, EME_NAMESPACE_ATTR_NAME, OMEMO_NS); 1238 | mxmlElementSetAttr(eme_node_p, EME_NAME_ATTR_NAME, "OMEMO"); 1239 | } 1240 | 1241 | store_node_p = mxmlNewElement(msg_p->message_node_p, STORE_NODE_NAME); 1242 | mxmlElementSetAttr(store_node_p, XMLNS_ATTR_NAME, HINTS_XMLNS); 1243 | 1244 | xml_str = mxmlSaveAllocString(msg_p->message_node_p, MXML_NO_CALLBACK); 1245 | if (!xml_str) { 1246 | ret_val = OMEMO_ERR; 1247 | goto cleanup; 1248 | } 1249 | 1250 | *msg_xml = xml_str; 1251 | 1252 | cleanup: 1253 | if (!ret_val) { 1254 | mxmlRemove(msg_p->header_node_p); 1255 | mxmlRemove(msg_p->payload_node_p); 1256 | } 1257 | mxmlDelete(body_node_p); 1258 | mxmlDelete(encrypted_node_p); 1259 | mxmlDelete(store_node_p); 1260 | mxmlDelete(eme_node_p); 1261 | 1262 | return ret_val; 1263 | } 1264 | 1265 | int omemo_message_prepare_decryption(char * incoming_message, omemo_message ** msg_pp) { 1266 | if (!incoming_message || !msg_pp) { 1267 | return OMEMO_ERR_NULL; 1268 | } 1269 | 1270 | int ret_val = 0; 1271 | mxml_node_t * message_node_p = (void *) 0; 1272 | mxml_node_t * body_node_p = (void *) 0; 1273 | mxml_node_t * eme_node_p = (void *) 0; 1274 | mxml_node_t * store_node_p = (void *) 0; 1275 | mxml_node_t * encrypted_node_p = (void *) 0; 1276 | mxml_node_t * header_node_p = (void *) 0; 1277 | mxml_node_t * payload_node_p = (void *) 0; 1278 | omemo_message * msg_p = (void *) 0; 1279 | 1280 | message_node_p = mxmlLoadString((void *) 0, incoming_message, MXML_OPAQUE_CALLBACK); 1281 | if (!message_node_p) { 1282 | log_err("incoming message is invalid XML: %s", incoming_message); 1283 | ret_val = OMEMO_ERR_MALFORMED_XML; 1284 | goto cleanup; 1285 | } 1286 | 1287 | body_node_p = mxmlFindPath(message_node_p, BODY_NODE_NAME); 1288 | if (body_node_p) { 1289 | ret_val = expect_next_node(body_node_p, mxmlGetParent, BODY_NODE_NAME, &body_node_p); 1290 | if (ret_val) { 1291 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_BODY_ELEM; 1292 | goto cleanup; 1293 | } 1294 | } 1295 | 1296 | eme_node_p = mxmlFindPath(message_node_p, EME_NODE_NAME); 1297 | 1298 | store_node_p = mxmlFindPath(message_node_p, STORE_NODE_NAME); 1299 | 1300 | encrypted_node_p = mxmlFindPath(message_node_p, ENCRYPTED_NODE_NAME); 1301 | if (!encrypted_node_p) { 1302 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_ENCRYPTED_ELEM; 1303 | goto cleanup; 1304 | } 1305 | 1306 | header_node_p = mxmlFindPath(encrypted_node_p, HEADER_NODE_NAME); 1307 | if (!header_node_p) { 1308 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_HEADER_ELEM; 1309 | goto cleanup; 1310 | } 1311 | 1312 | payload_node_p = mxmlFindPath(encrypted_node_p, PAYLOAD_NODE_NAME); 1313 | 1314 | msg_p = malloc(sizeof(omemo_message)); 1315 | if (!msg_p) { 1316 | ret_val = OMEMO_ERR_NOMEM; 1317 | goto cleanup; 1318 | } 1319 | memset(msg_p, 0, sizeof(omemo_message)); 1320 | 1321 | if (body_node_p) { 1322 | mxmlDelete(body_node_p); 1323 | } 1324 | 1325 | if (eme_node_p) { 1326 | mxmlDelete(eme_node_p); 1327 | } 1328 | 1329 | if (store_node_p) { 1330 | mxmlDelete(store_node_p); 1331 | } 1332 | 1333 | mxmlRemove(header_node_p); 1334 | msg_p->header_node_p = header_node_p; 1335 | 1336 | if (payload_node_p) { 1337 | payload_node_p = mxmlGetParent(payload_node_p); 1338 | mxmlRemove(payload_node_p); 1339 | msg_p->payload_node_p = payload_node_p; 1340 | } 1341 | 1342 | mxmlDelete(encrypted_node_p); 1343 | msg_p->message_node_p = message_node_p; 1344 | 1345 | *msg_pp = msg_p; 1346 | 1347 | cleanup: 1348 | if (ret_val) { 1349 | mxmlDelete(message_node_p); 1350 | free(msg_p); 1351 | } 1352 | return ret_val; 1353 | } 1354 | 1355 | int omemo_message_has_payload(omemo_message * msg_p) { 1356 | return (msg_p->payload_node_p) ? 1 : 0; 1357 | } 1358 | 1359 | uint32_t omemo_message_get_sender_id(omemo_message * msg_p) { 1360 | const char * sid_string = mxmlElementGetAttr(msg_p->header_node_p, HEADER_NODE_SID_ATTR_NAME); 1361 | return strtol(sid_string, (void *) 0, 0); 1362 | } 1363 | 1364 | static char * jid_strip_resource(const char * full_jid) { 1365 | char ** split = (void *) 0; 1366 | split = g_strsplit(full_jid, "/", -1); 1367 | char * ret = g_strndup(split[0], strlen(split[0])); 1368 | 1369 | g_strfreev(split); 1370 | 1371 | return ret; 1372 | } 1373 | 1374 | const char * omemo_message_get_sender_name_full(omemo_message * msg_p) { 1375 | return mxmlElementGetAttr(msg_p->message_node_p, MESSAGE_NODE_FROM_ATTR_NAME); 1376 | } 1377 | 1378 | char * omemo_message_get_sender_name_bare(omemo_message * msg_p) { 1379 | return jid_strip_resource(omemo_message_get_sender_name_full(msg_p)); 1380 | } 1381 | 1382 | const char * omemo_message_get_recipient_name_full(omemo_message * msg_p) { 1383 | return mxmlElementGetAttr(msg_p->message_node_p, MESSAGE_NODE_TO_ATTR_NAME); 1384 | } 1385 | 1386 | char * omemo_message_get_recipient_name_bare(omemo_message * msg_p) { 1387 | return jid_strip_resource(omemo_message_get_recipient_name_full(msg_p)); 1388 | } 1389 | 1390 | // Finds the key element for the given recipient device ID. 1391 | static int omemo_message_find_key_element(omemo_message * msg_p, uint32_t rid, mxml_node_t ** key_node_pp) { 1392 | if (!msg_p || !key_node_pp) { 1393 | return OMEMO_ERR_NULL; 1394 | } 1395 | 1396 | int ret_val = 0; 1397 | mxml_node_t * key_node_p = (void *) 0; 1398 | char * rid_string = (void *) 0; 1399 | 1400 | key_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, KEY_NODE_NAME, NULL, NULL, MXML_DESCEND); 1401 | if (!key_node_p) { 1402 | // if there is not at least one key, skip the rest of the function 1403 | ret_val = 0; 1404 | *key_node_pp = (void *) 0; 1405 | goto cleanup; 1406 | } 1407 | 1408 | if (int_to_string(rid, &rid_string) <= 0) { 1409 | ret_val = OMEMO_ERR_NOMEM; 1410 | goto cleanup; 1411 | } 1412 | 1413 | while (key_node_p) { 1414 | if (!strncmp(rid_string, mxmlElementGetAttr(key_node_p, KEY_NODE_RID_ATTR_NAME), strlen(rid_string))) { 1415 | *key_node_pp = key_node_p; 1416 | break; 1417 | } 1418 | 1419 | ret_val = expect_next_node(key_node_p, mxmlGetNextSibling, KEY_NODE_NAME, &key_node_p); 1420 | if (ret_val) { 1421 | key_node_p = (void *) 0; 1422 | ret_val = 0; 1423 | } 1424 | } 1425 | 1426 | 1427 | cleanup: 1428 | free(rid_string); 1429 | 1430 | return ret_val; 1431 | } 1432 | 1433 | int omemo_message_get_encrypted_key(omemo_message * msg_p, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ) { 1434 | if (!msg_p || !key_pp) { 1435 | return OMEMO_ERR_NULL; 1436 | } 1437 | 1438 | int ret_val = 0; 1439 | 1440 | mxml_node_t * key_node_p = (void *) 0; 1441 | const char * key_b64 = (void *) 0; 1442 | uint8_t * key_p = (void *) 0; 1443 | size_t key_len = 0; 1444 | 1445 | ret_val = omemo_message_find_key_element(msg_p, own_device_id, &key_node_p); 1446 | if (ret_val || !key_node_p) { 1447 | goto cleanup; 1448 | } 1449 | 1450 | key_b64 = mxmlGetOpaque(key_node_p); 1451 | if (!key_b64) { 1452 | *key_pp = (void *) 0; 1453 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_KEY_DATA; 1454 | goto cleanup; 1455 | } 1456 | 1457 | key_p = g_base64_decode(key_b64, &key_len); 1458 | 1459 | cleanup: 1460 | *key_pp = key_p; 1461 | *key_len_p = key_len; 1462 | 1463 | return ret_val; 1464 | } 1465 | 1466 | int omemo_message_is_encrypted_key_prekey(omemo_message * msg_p, uint32_t key_id, bool * is_prekey_p) { 1467 | if (!msg_p) { 1468 | return OMEMO_ERR_NULL; 1469 | } 1470 | 1471 | int ret_val = 0; 1472 | mxml_node_t * key_node_p = (void *) 0; 1473 | const char * prekey_attr_val = (void *) 0; 1474 | 1475 | ret_val = omemo_message_find_key_element(msg_p, key_id, &key_node_p); 1476 | if (ret_val || !key_node_p) { 1477 | *is_prekey_p = false; 1478 | goto cleanup; 1479 | } 1480 | 1481 | 1482 | prekey_attr_val = mxmlElementGetAttr(key_node_p, KEY_NODE_PREKEY_ATTR_NAME); 1483 | if (prekey_attr_val) { 1484 | // according to https://www.w3.org/TR/xmlschema-2/#boolean "1" can also be a boolean that means true 1485 | if (!strncmp(prekey_attr_val, KEY_NODE_PREKEY_ATTR_VAL_TRUE, strlen(KEY_NODE_PREKEY_ATTR_VAL_TRUE)) || !strncmp(prekey_attr_val, "1", strlen("1"))) { 1486 | *is_prekey_p = true; 1487 | goto cleanup; 1488 | } 1489 | } 1490 | 1491 | *is_prekey_p = false; 1492 | 1493 | cleanup: 1494 | return ret_val; 1495 | } 1496 | 1497 | int omemo_message_export_decrypted(omemo_message * msg_p, uint8_t * key_p, size_t key_len, const omemo_crypto_provider * crypto_p, char ** msg_xml_p) { 1498 | if (!msg_p || !msg_p->header_node_p || !msg_p->payload_node_p || !msg_p->message_node_p || !key_p || !crypto_p || !msg_xml_p) { 1499 | return OMEMO_ERR_NULL; 1500 | } 1501 | 1502 | int ret_val = 0; 1503 | 1504 | const char * payload_b64 = (void *) 0; 1505 | uint8_t * payload_p = (void *) 0; 1506 | size_t payload_len = 0; 1507 | mxml_node_t * iv_node_p = (void *) 0; 1508 | const char * iv_b64 = (void *) 0; 1509 | uint8_t * iv_p = (void *) 0; 1510 | size_t iv_len = 0; 1511 | size_t key_len_actual = 0; 1512 | size_t payload_len_actual = 0; 1513 | uint8_t * tag_p = (void *) 0; 1514 | uint8_t * pt_p = (void *) 0; 1515 | size_t pt_len = 0; 1516 | char * pt_str = (void *) 0; 1517 | mxml_node_t * body_node_p = (void *) 0; 1518 | char * xml = (void *) 0; 1519 | 1520 | payload_b64 = mxmlGetOpaque(msg_p->payload_node_p); 1521 | if (!payload_b64) { 1522 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_PAYLOAD_DATA; 1523 | goto cleanup; 1524 | } 1525 | payload_p = g_base64_decode(payload_b64, &payload_len); 1526 | 1527 | iv_node_p = mxmlFindElement(msg_p->header_node_p, msg_p->header_node_p, IV_NODE_NAME, NULL, NULL, MXML_DESCEND); 1528 | if (!iv_node_p) { 1529 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_IV_ELEM; 1530 | goto cleanup; 1531 | } 1532 | 1533 | iv_b64 = mxmlGetOpaque(iv_node_p); 1534 | if (!iv_b64) { 1535 | ret_val = OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_IV_DATA; 1536 | goto cleanup; 1537 | } 1538 | iv_p = g_base64_decode(iv_b64, &iv_len); 1539 | 1540 | if (key_len == OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH) { 1541 | key_len_actual = OMEMO_AES_128_KEY_LENGTH; 1542 | payload_len_actual = payload_len; 1543 | tag_p = key_p + OMEMO_AES_128_KEY_LENGTH; 1544 | } else if (key_len == OMEMO_AES_128_KEY_LENGTH) { 1545 | key_len_actual = key_len; 1546 | payload_len_actual = payload_len - OMEMO_AES_GCM_TAG_LENGTH; 1547 | tag_p = payload_p + (payload_len - OMEMO_AES_GCM_TAG_LENGTH); 1548 | } else { 1549 | ret_val = OMEMO_ERR_UNSUPPORTED_KEY_LEN; 1550 | goto cleanup; 1551 | } 1552 | 1553 | ret_val = crypto_p->aes_gcm_decrypt_func(payload_p, payload_len_actual, 1554 | iv_p, iv_len, 1555 | key_p, key_len_actual, 1556 | tag_p, OMEMO_AES_GCM_TAG_LENGTH, 1557 | crypto_p->user_data_p, 1558 | &pt_p, &pt_len); 1559 | if (ret_val) { 1560 | goto cleanup; 1561 | } 1562 | 1563 | pt_str = malloc(pt_len + 1); 1564 | if (!pt_str) { 1565 | ret_val = OMEMO_ERR_NOMEM; 1566 | goto cleanup; 1567 | } 1568 | memcpy(pt_str, pt_p, pt_len); 1569 | pt_str[pt_len] = '\0'; 1570 | 1571 | body_node_p = mxmlNewElement(MXML_NO_PARENT, BODY_NODE_NAME); 1572 | (void) mxmlNewText(body_node_p, 0, pt_str); 1573 | 1574 | 1575 | mxmlAdd(msg_p->message_node_p, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, body_node_p); 1576 | 1577 | xml = mxmlSaveAllocString(msg_p->message_node_p, MXML_NO_CALLBACK); 1578 | if (!xml) { 1579 | ret_val = OMEMO_ERR_NOMEM; 1580 | goto cleanup; 1581 | } 1582 | 1583 | *msg_xml_p = xml; 1584 | 1585 | cleanup: 1586 | g_free(payload_p); 1587 | g_free(iv_p); 1588 | free(pt_p); 1589 | free(pt_str); 1590 | mxmlDelete(body_node_p); 1591 | 1592 | return ret_val; 1593 | } 1594 | 1595 | void omemo_message_destroy(omemo_message * msg_p) { 1596 | if (msg_p) { 1597 | mxmlDelete(msg_p->message_node_p); 1598 | mxmlDelete(msg_p->header_node_p); 1599 | mxmlDelete(msg_p->payload_node_p); 1600 | if (msg_p->key_p) { 1601 | memset(msg_p->key_p, 0, msg_p->key_len); 1602 | free(msg_p->key_p); 1603 | } 1604 | if (msg_p->iv_p) { 1605 | memset(msg_p->iv_p, 0, msg_p->iv_len); 1606 | free(msg_p->iv_p); 1607 | } 1608 | } 1609 | } 1610 | -------------------------------------------------------------------------------- /src/libomemo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * LIBOMEMO 0.8.1 3 | */ 4 | 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | typedef struct omemo_bundle omemo_bundle; 14 | typedef struct omemo_devicelist omemo_devicelist; 15 | typedef struct omemo_message omemo_message; 16 | 17 | typedef struct omemo_crypto_provider { 18 | /** 19 | * Gets cryptographically strong preudo-random bytes. 20 | * 21 | * @param buf_p Will point to a buffer with the output bytes. free() when done. 22 | * @param buf_len The length of the buffer, or rather how many random bytes are written into the buffer. 23 | * @param user_data_p Pointer to the user data set in the crypto provider. 24 | * @return 0 on success, negative on error. 25 | */ 26 | int (*random_bytes_func)(uint8_t ** buf_pp, size_t buf_len, void * user_data_p); 27 | 28 | /** 29 | * Encrypts a byte buffer. 30 | * 31 | * @param plaintext_p Pointer to the plaintext buffer. 32 | * @param plaintext_len The length of the buffer. 33 | * @param iv_p Pointer to the IV buffer. 34 | * @param iv_len Length of the IV buffer. 35 | * @param key_p Pointer to the key buffer. 36 | * @param key_len Length of the key buffer. 37 | * @param tag_len Length of the tag to generate. 38 | * @param user_data_p Pointer to the user data set in the crypto provider. 39 | * @param ciphertext_pp Will point to a pointer to the ciphertext buffer. 40 | * @param ciphertext_len_p Will point to the length of the ciphertext buffer. 41 | * @param tag_pp Will point to a pointer to the tag buffer. 42 | * @return 0 on success, negative on error. 43 | */ 44 | int (*aes_gcm_encrypt_func)(const uint8_t * plaintext_p, size_t plaintext_len, 45 | const uint8_t * iv_p, size_t iv_len, 46 | const uint8_t * key_p, size_t key_len, 47 | size_t tag_len, 48 | void * user_data_p, 49 | uint8_t ** ciphertext_pp, size_t * ciphertext_len_p, 50 | uint8_t ** tag_pp); 51 | 52 | /** 53 | * Decrypts a ciphertext byte buffer. 54 | * 55 | * @param ciphertext_p Pointer to the ciphertext buffer. 56 | * @param ciphertext_len Length of the ciphertext buffer. 57 | * @param iv_p Pointer to the IV buffer. 58 | * @param iv_len Length of the IV buffer. 59 | * @param key_p Pointer to the key buffer. 60 | * @param key_len Length of the key buffer. 61 | * @param tag_p Pointer to the tag buffer. 62 | * @param tag_len Length of the tag buffer. 63 | * @param user_data_p Pointer to the user data set in the crypto provider. 64 | * @param plaintext_pp Will point to a pointer to the plaintext. 65 | * @param Will point to the length of the plaintext buffer. 66 | * @return 0 on success, negative on error. 67 | */ 68 | int (*aes_gcm_decrypt_func)(const uint8_t * ciphertext_p, size_t ciphertext_len, 69 | const uint8_t * iv_p, size_t iv_len, 70 | const uint8_t * key_p, size_t key_len, 71 | uint8_t * tag_p, size_t tag_len, 72 | void * user_data_p, 73 | uint8_t ** plaintext_pp, size_t * plaintext_len_p); 74 | 75 | /** 76 | * Pointer to the user data that will be passed to the functions. 77 | */ 78 | void * user_data_p; 79 | } omemo_crypto_provider; 80 | 81 | #define OMEMO_AES_128_KEY_LENGTH 16 82 | #define OMEMO_AES_GCM_IV_LENGTH 12 83 | #define OMEMO_AES_GCM_TAG_LENGTH 16 84 | 85 | #define OMEMO_LOG_OFF -1 86 | #define OMEMO_LOG_ERROR 0 87 | #define OMEMO_LOG_WARNING 1 88 | #define OMEMO_LOG_NOTICE 2 89 | #define OMEMO_LOG_INFO 3 90 | #define OMEMO_LOG_DEBUG 4 91 | 92 | #define OMEMO_SUCCESS 0 93 | #define OMEMO_ERR -10000 94 | #define OMEMO_ERR_NOMEM -10001 95 | #define OMEMO_ERR_NULL -10002 96 | #define OMEMO_ERR_CRYPTO -10010 97 | #define OMEMO_ERR_AUTH_FAIL -10020 98 | #define OMEMO_ERR_UNSUPPORTED_KEY_LEN -10030 99 | #define OMEMO_ERR_STORAGE -10100 100 | 101 | // the errors below were initially all equal to the first one 102 | #define OMEMO_ERR_MALFORMED_BUNDLE -11000 103 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_ITEMS_ELEM -11001 104 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_NODE_ATTR -11002 105 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_ITEM_ELEM -11003 106 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_BUNDLE_ELEM -11004 107 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_ELEM -11005 108 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_DATA -11006 109 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_SPK_ID_ATTR -11007 110 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_SIG_ELEM -11008 111 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_SIG_DATA -11009 112 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_IK_ELEM -11010 113 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_IK_DATA -11011 114 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEYS_ELEM -11012 115 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_ELEM -11013 116 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_DATA -11014 117 | #define OMEMO_ERR_MALFORMED_BUNDLE_NO_PREKEY_ID_ATTR -11015 118 | 119 | // the errors below were also initially all equal to the first one 120 | #define OMEMO_ERR_MALFORMED_XML -12000 121 | #define OMEMO_ERR_MALFORMED_DEVICELIST_NO_ITEMS_ELEM -12101 122 | #define OMEMO_ERR_MALFORMED_DEVICELIST_NO_ITEM_ELEM -12102 123 | #define OMEMO_ERR_MALFORMED_DEVICELIST_NO_LIST_ELEM -12103 124 | #define OMEMO_ERR_MALFORMED_DEVICELIST_NO_DEVICE_ID_ATTR -12104 125 | #define OMEMO_ERR_MALFORMED_OUTGOING_MESSAGE_NO_BODY_ELEM -12201 126 | #define OMEMO_ERR_MALFORMED_OUTGOING_MESSAGE_NO_BODY_DATA -12202 127 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_BODY_ELEM -12301 128 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_ENCRYPTED_ELEM -12302 129 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_HEADER_ELEM -12303 130 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_PAYLOAD_DATA -12304 131 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_IV_ELEM -12305 132 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_IV_DATA -12306 133 | #define OMEMO_ERR_MALFORMED_INCOMING_MESSAGE_NO_KEY_DATA -12307 134 | 135 | #define OMEMO_ADD_MSG_NONE 0 136 | #define OMEMO_ADD_MSG_BODY 1 137 | #define OMEMO_ADD_MSG_EME 2 138 | #define OMEMO_ADD_MSG_BOTH 3 139 | 140 | #define OMEMO_STRIP_ALL 1 141 | #define OMEMO_STRIP_NONE 0 142 | 143 | #define omemo_devicelist_list_data(X) (*((uint32_t *) X->data)) 144 | 145 | /*-------------------- BUNDLE --------------------*/ 146 | 147 | /** 148 | * Creates a fresh bundle. 149 | * 150 | * @param bundle_pp Will be set to the allocated bundle. 151 | * @return 0 on success, negative on error. 152 | */ 153 | int omemo_bundle_create(omemo_bundle ** bundle_pp); 154 | 155 | /** 156 | * Sets the device id of a bundle. 157 | * 158 | * @param bundle_p Pointer to the bundle. 159 | * @param device_id The device id to set. 160 | * @return 0 on success, negative on error. 161 | */ 162 | int omemo_bundle_set_device_id(omemo_bundle * bundle_p, uint32_t device_id); 163 | 164 | /** 165 | * Gets the device id of a bundle. 166 | * 167 | * @param bundle_p Pointer to the bundle. 168 | * @return The device id. 169 | */ 170 | uint32_t omemo_bundle_get_device_id(omemo_bundle * bundle_p); 171 | 172 | /** 173 | * Sets the signed pre key of a bundle. 174 | * 175 | * @param bundle_p Pointer to the bundle. 176 | * @param pre_key_id The ID of the pre key. 177 | * @param data_p Pointer to the serialized key data. 178 | * @param data_len Length of the data. 179 | * @return 0 on success, negative on error. 180 | */ 181 | int omemo_bundle_set_signed_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id, uint8_t * data_p, size_t data_len); 182 | 183 | /** 184 | * Gets the signed pre key from the specified bundle. 185 | * 186 | * @param bundle_p Pointer to the bundle. 187 | * @param pre_key_id_p Will be set to the pre key id as returned by strtol. 188 | * @param data_pp Will be set to a pointer to the serialized key data. Has to be free()d when done. 189 | * @param data_len_p Will be set to the length of the data. 190 | * @return 0 on success, negative on error. 191 | */ 192 | int omemo_bundle_get_signed_pre_key(omemo_bundle * bundle_p, uint32_t * pre_key_id_p, uint8_t ** data_pp, size_t * data_len_p); 193 | 194 | /** 195 | * Sets the signature data belonging to the signed pre key. 196 | * 197 | * @param bundle_p Pointer to the bundle. 198 | * @param data_p Pointer to the serialized data. 199 | * @param data_len Length of the data. 200 | * @return 0 on success, negative on error. 201 | */ 202 | int omemo_bundle_set_signature(omemo_bundle * bundle_p, uint8_t * data_p, size_t data_len); 203 | 204 | /** 205 | * Gets the signature from the specified bundle. 206 | * 207 | * @param bundle_p Pointer to the bundle. 208 | * @param data_pp Will be set to a pointer to the signature data. Has to be free()d when done. 209 | * @param data_len_p Will be set to the length of the data. 210 | * @return 0 on succcess, negative on error. 211 | */ 212 | int omemo_bundle_get_signature(omemo_bundle * bundle_p, uint8_t ** data_pp, size_t * data_len_p); 213 | 214 | /** 215 | * Sets the identity key. 216 | * 217 | * @param bundle_p Pointer to the bundle. 218 | * @param data_p Pointer to the serialized key data. 219 | * @param data_len Length of the data. 220 | * @return 0 on success, negative on error. 221 | */ 222 | int omemo_bundle_set_identity_key(omemo_bundle * bundle_p, uint8_t * data_p, size_t data_len); 223 | 224 | /** 225 | * Gets the identity key from the specified bundle. 226 | * 227 | * @param bundle_p Pointer to the bundle. 228 | * @param data_pp Will be set to a pointer to the identity key data. Has to be free()d when done. 229 | * @param data_len_p Will be set to the length of the data. 230 | * @return 0 on success, negative on error. 231 | */ 232 | int omemo_bundle_get_identity_key(omemo_bundle * bundle_p, uint8_t ** data_pp, size_t * data_len_p); 233 | 234 | /** 235 | * Adds a pre key to the bundle. 236 | * 237 | * @param bundle_p Pointer to the bundle. 238 | * @param pre_key_id The ID of the pre key. 239 | * @param data_p Pointer to the serialized public key data. 240 | * @param data_len Length of the data. 241 | * @return 0 on success, negative on error. 242 | */ 243 | int omemo_bundle_add_pre_key(omemo_bundle * bundle_p, uint32_t pre_key_id, uint8_t * data_p, size_t data_len); 244 | 245 | /** 246 | * Gets a random pre key from the specified bundle. 247 | * 248 | * @param bundle_p Pointer to the bundle. 249 | * @param pre_key_id_p Will be set to the ID of the selected pre key. 250 | * @param data_pp Will be set to a pointer to the serialized public key data. Has to be free()d when done. 251 | * @param data_len_p Will be set to the length of the data. 252 | * @return 0 on success, negative on error. 253 | */ 254 | int omemo_bundle_get_random_pre_key(omemo_bundle * bundle_p, uint32_t * pre_key_id_p, uint8_t ** data_pp, size_t * data_len_p); 255 | 256 | /** 257 | * "Exports" the bundle into XML for publishing via PEP. 258 | * 259 | * @param bundle_p Pointer to the complete bundle with at least the minimum amount of prekeys. 260 | * @param publish Will be set to the XML string starting at the node. 261 | * @return 0 on success, negative on error. 262 | */ 263 | int omemo_bundle_export(omemo_bundle * bundle_p, char ** publish); 264 | 265 | /** 266 | * Parses the received XML bundle information and returns a bundle struct to work with. 267 | * Does some basic validity checks and returns OMEMO_ERR_MALFORMED_XML if something is wrong. 268 | * 269 | * @param received_bundle The bundle XML, starting at the node. 270 | * @param bundle_pp Will be set to the omemo_bundle, has to be freed when done. 271 | * @return 0 on success, negative on error. 272 | */ 273 | int omemo_bundle_import (const char * received_bundle, omemo_bundle ** bundle_pp); 274 | 275 | /** 276 | * Get the node name of the bundle node. 277 | * 278 | * @param device_id The registration ID of the device whose bundle is to be requested. 279 | * @param node_name_p Will be set to the node name. Has to be free()d afterwards. 280 | * @return 0 on success, negative on error 281 | */ 282 | int omemo_bundle_get_pep_node_name(uint32_t device_id, char ** node_name_p); 283 | 284 | /** 285 | * Frees the memory of everything contained in a bundle as well as the bundle itself. 286 | * 287 | * @param bundle_p Pointer to the bundle to free. 288 | */ 289 | void omemo_bundle_destroy(omemo_bundle * bundle_p); 290 | 291 | 292 | /*-------------------- DEVICELIST --------------------*/ 293 | 294 | /** 295 | * Creates a fresh devicelist. Usually only useful if there are no other OMEMO devices yet. 296 | * 297 | * @param from Owner of the devices on the list ("from" attribute in the XML tag). 298 | * @param dl_pp Will be set to a pointer to the devicelist struct. 299 | * @return 0 on success, negative on error. 300 | */ 301 | int omemo_devicelist_create(const char * from, omemo_devicelist ** dl_pp); 302 | 303 | /** 304 | * Imports a devicelist from an XML string to the internal representation. 305 | * 306 | * @param received_devicelist The devicelist as received by the PEP update, starting at the node. 307 | * @param from The owner of the devicelist ("from" attribute of the stanza). 308 | * @param dl_pp Will be set to the pointer to the created devicelist struct. 309 | * @return 0 on success, negative on error. 310 | */ 311 | int omemo_devicelist_import(char * received_devicelist, const char * from, omemo_devicelist ** dl_pp); 312 | 313 | /** 314 | * Adds a device to a devicelist (e.g. the own device to the own list). 315 | * 316 | * @param dl_p Pointer to an initialized devicelist. 317 | * @param device_id The ID to add to the list. 318 | * @return 0 on success, negative on error. 319 | */ 320 | int omemo_devicelist_add(omemo_devicelist * dl_p, uint32_t device_id); 321 | 322 | /** 323 | * Looks for the given device ID in the given device list. 324 | * 325 | * @param dl_p Pointer to an initialized devicelist. 326 | * @param device_id The device ID to look for. 327 | * @return 1 if the list contains the ID, 0 if it does not or is NULL. 328 | */ 329 | int omemo_devicelist_contains_id(const omemo_devicelist * dl_p, uint32_t device_id); 330 | 331 | /** 332 | * Removes a device ID from the list. 333 | * If it is not contained in the list, nothing happens. 334 | * 335 | * @param dl_p Pointer to the devicelist. 336 | * @param device_id The ID to remove from the list. 337 | * @return 0 on success, negative on error. 338 | */ 339 | int omemo_devicelist_remove(omemo_devicelist * dl_p, uint32_t device_id); 340 | 341 | /** 342 | * @return 1 if the devicelist containts no IDs, 0 otherwise. 343 | */ 344 | int omemo_devicelist_is_empty(omemo_devicelist * dl_p); 345 | 346 | /** 347 | * Compares two devicelists. 348 | * If two empty devicelists are compared, the returned pointers will be NULL. 349 | * This does not mean that any of the pointers may be NULL. 350 | * 351 | * @param dl_a_p Pointer to devicelist A. 352 | * @param dl_b_p Pointer to devicelist B. 353 | * @param a_minus_b_pp Will be set to a list of IDs that are contained in A, but not in B (i.e. A\B). 354 | * @param b_minus_a_pp Will be set to a list of IDs that are contained in B, but not in A (i.e. B\A). 355 | * @return 0 on success, negative on error. 356 | */ 357 | int omemo_devicelist_diff(const omemo_devicelist * dl_a_p, const omemo_devicelist * dl_b_p, GList ** a_minus_b_pp, GList ** b_minus_a_pp); 358 | 359 | /** 360 | * Exports the devicelist to an XML string, as needed to publish it via PEP. 361 | * 362 | * @param dl_p Pointer to an initialized devicelist. 363 | * @param xml_p Will point to the XML string (starting at the node). 364 | * @return 0 on success, negative on error. 365 | */ 366 | int omemo_devicelist_export(omemo_devicelist * dl_p, char ** xml_p); 367 | 368 | /** 369 | * Returns a copy of the internally kept list of IDs for easy iterating. 370 | * Has to be freed using g_list_free_full(list_p, free). 371 | * 372 | * "data" is a pointer to the uint32_t, so to acces it either: 373 | * - cast the pointer to an uint32_t * and then dereference it 374 | * - use the omemo_devicelist_list_data() macro that does the same 375 | * 376 | * @param dl_p Pointer to the devicelist. 377 | * @return Pointer to the head of the list, which may be null. 378 | */ 379 | GList * omemo_devicelist_get_id_list(const omemo_devicelist * dl_p); 380 | 381 | /** 382 | * Checks if the devicelist struct contains any IDs. 383 | * 384 | * @param dl_p Pointer to the devicelist. 385 | * @return 1 if it contains at least 1 ID, 0 if it does not (i.e. the id_list is NULL). 386 | */ 387 | int omemo_devicelist_has_id_list(const omemo_devicelist * dl_p); 388 | 389 | /** 390 | * Returns the name of the devicelist owner. 391 | * 392 | * @param Pointer to the devicelist. 393 | * @return The saved name of the owner. 394 | */ 395 | const char * omemo_devicelist_get_owner(const omemo_devicelist * dl_p); 396 | 397 | /** 398 | * Assembles the name of the devicelist PEP node. 399 | * As it is static, it can also be done statically from the constants. 400 | * This function only exists to have the same interface as for the bundle node name. 401 | * 402 | * @param node_name_p Will be set to a string that contains the necessary name. 403 | * Has to be free()d when done. 404 | * @return 0 on success, negative on error. 405 | */ 406 | int omemo_devicelist_get_pep_node_name(char ** node_name_p); 407 | 408 | /** 409 | * Frees the memory used by a devicelist struct, and of all it contains. 410 | * 411 | * @param dl_p Pointer to the devicelist to destroy. 412 | */ 413 | void omemo_devicelist_destroy(omemo_devicelist * dl_p); 414 | 415 | 416 | /*-------------------- MESSAGE --------------------*/ 417 | 418 | /** 419 | * Creates a message without a payload for use as a KeyTransportElement. 420 | * 421 | * @param sender_device_id The own device ID. 422 | * @param crypto_p Pointer to a crypro provider. 423 | * @param message_pp Will point to the created message struct. 424 | * @return 0 on success, negative on error. 425 | */ 426 | int omemo_message_create(uint32_t sender_device_id, const omemo_crypto_provider * crypto_p, omemo_message ** message_pp); 427 | 428 | /** 429 | * Strips the message of XEP-0071: XHTML-IM nodes, and additional nodes which are valid 430 | * through different values for the xml:lang attribute. 431 | * Leaks plaintext if this is not done one way or the other and the clients supports these! 432 | * 433 | * @param msg_p Pointer to the omemo_message to strip of possible additional plaintext. 434 | * @return 0 on success, negative on error. 435 | */ 436 | int omemo_message_strip_possible_plaintext(omemo_message * msg_p); 437 | 438 | /** 439 | * Prepares an intercepted stanza for encryption. 440 | * This means it removes the and encrypts the contained text, leaving everything else as it is. 441 | * Recipient devices have to be added to the resulting struct before it is exported back to xml. 442 | * 443 | * @param outgoing_message The intercepted stanza. 444 | * @param sender_device_id The own device ID. 445 | * @param crypto_p Pointer to a crypto provider. 446 | * @param strip Either OMEMO_STRIP_ALL or OMEMO_STRIP_NONE. If the former, applies omemo_message_strip_possible_plaintext(). 447 | * @param message_pp Will be set to a pointer to the message struct. 448 | * @return 0 on success, negative on error. 449 | */ 450 | int omemo_message_prepare_encryption(char * outgoing_message, uint32_t sender_device_id, const omemo_crypto_provider * crypto_p, int strip, omemo_message ** message_pp); 451 | 452 | /** 453 | * Gets the symmetric encryption key and appended authentication tag from the message struct 454 | * so that both can be encrypted with the Signal session. 455 | * 456 | * Naturally only exists in an outgoing message. 457 | * 458 | * @param msg_p Pointer to the message struct. 459 | * @return Pointer to the key data, or null if it does not exist. 460 | */ 461 | const uint8_t * omemo_message_get_key(omemo_message * msg_p); 462 | 463 | 464 | /** 465 | * Gets the length of the symmetric key and appended tag. 466 | * Again, this only makes sense on an outgoing message. 467 | * 468 | * At the moment, the length is fixed to 16 (128 bits) for the key, and another 16 for the tag. 469 | * In a KeyTransportElement, no tag exists. 470 | * 471 | * @param msg_p Pointer to the message struct. 472 | * @return Length of the key + tag in bytes. 473 | */ 474 | size_t omemo_message_get_key_len(omemo_message * msg_p); 475 | 476 | /** 477 | * Add the encrypted symmetric key for a specific device id to the message. 478 | * Only makes sense on outgoing messages. 479 | * 480 | * @param msg_p Pointer to the message to add to. 481 | * @param device_id The recipient device ID. 482 | * @param encrypted_key_p The encrypted key data. 483 | * @param key_len Length of the encrypted key data. 484 | * @return 0 on success, negative on error. 485 | */ 486 | int omemo_message_add_recipient(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len); 487 | 488 | /** 489 | * Add the encrypted symmetric key for a specific device id to the message with the prekey attribute. 490 | * This notifies the recipient device of the fact that this is the very first message of the session, 491 | * which therefore still has to be estalished on its side. 492 | * Only makes sense on outgoing messages. 493 | * 494 | * @param msg_p Pointer to the message to add to. 495 | * @param device_id The recipient device ID. 496 | * @param encrypted_key_p The encrypted key data. 497 | * @param key_len Length of the encrypted key data. 498 | * @return 0 on success, negative on error. 499 | */ 500 | int omemo_message_add_recipient_w_prekey(omemo_message * msg_p, uint32_t device_id, const uint8_t * encrypted_key_p, size_t key_len); 501 | 502 | /** 503 | * After all recipients have been added, this function can be used to export the resulting stanza. 504 | * Also adds a hint. 505 | * 506 | * @param msg_p Pointer to the message. 507 | * @param add_msg One of ADD_MSG_* constants. For optionally adding a body, EME, or both. 508 | * @param msg_xml Will be set to the resulting xml string. free() when done with it. 509 | * @return 0 on success, negative on error. 510 | */ 511 | int omemo_message_export_encrypted(omemo_message * msg_p, int add_msg, char ** msg_xml); 512 | 513 | /** 514 | * Prepares an intercepted stanza for decryption by parsing it. 515 | * Afterwards, the encrypted symmetric key can be retrieved, and the decrypted key used to decrypt the payload. 516 | * 517 | * @param incoming_message The incoming stanza xml as a string. 518 | * @param msg_pp Will be set to the created message. 519 | * @return 0 on success, negative on error. 520 | */ 521 | int omemo_message_prepare_decryption(char * incoming_message, omemo_message ** msg_pp); 522 | 523 | /** 524 | * Checks if the message has a payload, i.e. whether it is a MessageElement or KeyTransportElement. 525 | * 526 | * @param msg_p Pointer to the message. 527 | * @return 1 if has a payload, 0 if it does not. 528 | */ 529 | int omemo_message_has_payload(omemo_message * msg_p); 530 | 531 | /** 532 | * Gets the sender's device id from an OMEMO message. 533 | * 534 | * @param msg_p Pointer to the message. 535 | * @return The sid. 536 | */ 537 | uint32_t omemo_message_get_sender_id(omemo_message * msg_p); 538 | 539 | /** 540 | * Gets the sender's full JID. 541 | * Note that there is no "from" attribute in outgoing messages. 542 | * 543 | * @param msg_p Pointer to the message. 544 | * @return The full JID. 545 | */ 546 | const char * omemo_message_get_sender_name_full(omemo_message * msg_p); 547 | 548 | /** 549 | * Gets the sender's bare JID. 550 | * Note that there is no "from" attribute in outgoing messages. 551 | * 552 | * @param msg_p Pointer to the message. 553 | * @return The bare JID. Has to be free()d. 554 | */ 555 | char * omemo_message_get_sender_name_bare(omemo_message * msg_p); 556 | 557 | /** 558 | * Gets the recipient's full JID. 559 | * 560 | * @param msg_p Pointer to the message. 561 | * @return The full JID. 562 | */ 563 | const char * omemo_message_get_recipient_name_full(omemo_message * msg_p); 564 | 565 | /** 566 | * Gets the recipient's bare JID. 567 | * 568 | * @param msg_p Pointer to the message. 569 | * @return The bare JID. 570 | */ 571 | char * omemo_message_get_recipient_name_bare(omemo_message * msg_p); 572 | 573 | /** 574 | * Gets the encrypted key data for a specific device ID (usually your own so you can decrypt it). 575 | * 576 | * @param msg_p Pointer to the message. 577 | * @param own_device_id The device ID to get the encrypted key for. 578 | * @param key_pp Will be set to the encrypted key data, or NULL if there is no data for the specified ID. 579 | * Has to be free()d. 580 | * @param key_len_p Will be set to length of encrypted key data. 581 | * @return 0 on success, negative on error. Note that success does not mean a key was found. 582 | */ 583 | int omemo_message_get_encrypted_key(omemo_message * msg_p, uint32_t own_device_id, uint8_t ** key_pp, size_t * key_len_p ); 584 | 585 | /** 586 | * Returns whether the key with the speicified device ID is a prekey. 587 | * 588 | * @param msg_p Pointer to the message. 589 | * @param recipient_device_id The device ID to check. 590 | * @param is_prekey_p Will be set to true if the key is a prekey, false otherwise. 591 | * @return int 0 on success, negative on error. 592 | */ 593 | int omemo_message_is_encrypted_key_prekey(omemo_message * msg_p, uint32_t recipient_device_id, bool * is_prekey_p); 594 | 595 | /** 596 | * Using the decrypted symmetric key, this method decrypts the payload and exports the original stanza. 597 | * 598 | * @param msg_p Pointer to the message. 599 | * @param key_p Pointer to the decrypted symmetric key. 600 | * @param key_len Length of the key data. 601 | * @param crypto_p Pointer to the crypto provider. 602 | * @param msg_xml_p Will be set to the xml string. 603 | * @return 0 on success, negative on error. 604 | */ 605 | int omemo_message_export_decrypted(omemo_message * msg_p, uint8_t * key_p, size_t key_len, const omemo_crypto_provider * crypto_p, char ** msg_xml_p); 606 | 607 | /** 608 | * Frees the memory of everything contained in the message struct as well as the struct itself. 609 | * 610 | * @param msg_p Pointer to the message. 611 | */ 612 | void omemo_message_destroy(omemo_message * msg_p); 613 | -------------------------------------------------------------------------------- /src/libomemo_crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "libomemo.h" 7 | 8 | 9 | void omemo_default_crypto_init(void) { 10 | (void) gcry_check_version((void *) 0); 11 | gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN); 12 | gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); 13 | gcry_control (GCRYCTL_RESUME_SECMEM_WARN); 14 | gcry_control(GCRYCTL_USE_SECURE_RNDPOOL); 15 | gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 16 | 17 | } 18 | 19 | int omemo_default_crypto_random_bytes(uint8_t ** buf_pp, size_t buf_len, void * user_data_p) { 20 | (void) user_data_p; 21 | 22 | if (!buf_pp) { 23 | return OMEMO_ERR_NULL; 24 | } 25 | 26 | uint8_t * buf_p = malloc(sizeof(uint8_t) * buf_len); 27 | if (!buf_p) { 28 | return OMEMO_ERR_NOMEM; 29 | } 30 | 31 | gcry_randomize(buf_p, buf_len, GCRY_STRONG_RANDOM); 32 | 33 | *buf_pp = buf_p; 34 | 35 | return 0; 36 | } 37 | 38 | int omemo_default_crypto_aes_gcm_encrypt( const uint8_t * plaintext_p, size_t plaintext_len, 39 | const uint8_t * iv_p, size_t iv_len, 40 | const uint8_t * key_p, size_t key_len, 41 | size_t tag_len, 42 | void * user_data_p, 43 | uint8_t ** ciphertext_pp, size_t * ciphertext_len_p, 44 | uint8_t ** tag_pp) { 45 | (void) user_data_p; 46 | 47 | if (!plaintext_p || !iv_p || !key_p || !ciphertext_pp || !ciphertext_len_p) { 48 | return OMEMO_ERR_NULL; 49 | } 50 | 51 | int ret_val = 0; 52 | int hd_is_init = 0; 53 | 54 | int algo = 0; 55 | gcry_cipher_hd_t cipher_hd = NULL; 56 | uint8_t * out_p = (void *) 0; 57 | uint8_t * tag_p = (void *) 0; 58 | 59 | switch(key_len) { 60 | case 16: 61 | algo = GCRY_CIPHER_AES128; 62 | break; 63 | case 24: 64 | algo = GCRY_CIPHER_AES192; 65 | break; 66 | case 32: 67 | algo = GCRY_CIPHER_AES256; 68 | break; 69 | default: 70 | ret_val = OMEMO_ERR_CRYPTO; 71 | goto cleanup; 72 | } 73 | 74 | ret_val = gcry_cipher_open(&cipher_hd, algo, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE); 75 | if (ret_val) { 76 | ret_val = -ret_val; 77 | goto cleanup; 78 | } 79 | hd_is_init = 1; 80 | 81 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 82 | if (ret_val) { 83 | ret_val = -ret_val; 84 | goto cleanup; 85 | } 86 | 87 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 88 | if (ret_val) { 89 | ret_val = -ret_val; 90 | goto cleanup; 91 | } 92 | 93 | out_p = malloc(sizeof(uint8_t) * plaintext_len); 94 | if (!out_p) { 95 | ret_val = OMEMO_ERR_NOMEM; 96 | goto cleanup; 97 | } 98 | 99 | ret_val = gcry_cipher_encrypt(cipher_hd, out_p, plaintext_len, plaintext_p, plaintext_len); 100 | if (ret_val) { 101 | ret_val = -ret_val; 102 | goto cleanup; 103 | } 104 | 105 | tag_p = malloc(sizeof(uint8_t) * tag_len); 106 | if (!tag_p) { 107 | ret_val = OMEMO_ERR_NOMEM; 108 | goto cleanup; 109 | } 110 | 111 | ret_val = gcry_cipher_gettag(cipher_hd, tag_p, tag_len); 112 | if (ret_val) { 113 | ret_val = -ret_val; 114 | goto cleanup; 115 | } 116 | 117 | *ciphertext_pp = out_p; 118 | *ciphertext_len_p = plaintext_len; 119 | *tag_pp = tag_p; 120 | 121 | cleanup: 122 | if (ret_val) { 123 | free(out_p); 124 | free(tag_p); 125 | } 126 | if (hd_is_init) { 127 | gcry_cipher_close(cipher_hd); 128 | } 129 | 130 | return ret_val; 131 | } 132 | 133 | int omemo_default_crypto_aes_gcm_decrypt( const uint8_t * ciphertext_p, size_t ciphertext_len, 134 | const uint8_t * iv_p, size_t iv_len, 135 | const uint8_t * key_p, size_t key_len, 136 | uint8_t * tag_p, size_t tag_len, 137 | void * user_data_p, 138 | uint8_t ** plaintext_pp, size_t * plaintext_len_p) { 139 | (void) user_data_p; 140 | 141 | if (!ciphertext_p || !iv_p || !key_p || !tag_p || !plaintext_pp || !plaintext_len_p) { 142 | return OMEMO_ERR_NULL; 143 | } 144 | 145 | int ret_val = 0; 146 | int hd_is_init = 0; 147 | 148 | int algo = 0; 149 | gcry_cipher_hd_t cipher_hd = NULL; 150 | uint8_t * out_p = (void *) 0; 151 | 152 | switch(key_len) { 153 | case 16: 154 | algo = GCRY_CIPHER_AES128; 155 | break; 156 | case 24: 157 | algo = GCRY_CIPHER_AES192; 158 | break; 159 | case 32: 160 | algo = GCRY_CIPHER_AES256; 161 | break; 162 | default: 163 | ret_val = OMEMO_ERR_CRYPTO; 164 | goto cleanup; 165 | } 166 | 167 | ret_val = gcry_cipher_open(&cipher_hd, algo, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE); 168 | if (ret_val) { 169 | ret_val = -ret_val; 170 | goto cleanup; 171 | } 172 | hd_is_init = 1; 173 | 174 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 175 | if (ret_val) { 176 | ret_val = -ret_val; 177 | goto cleanup; 178 | } 179 | 180 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 181 | if (ret_val) { 182 | ret_val = -ret_val; 183 | goto cleanup; 184 | } 185 | 186 | out_p = malloc(sizeof(uint8_t) * ciphertext_len); 187 | if (!out_p) { 188 | ret_val = OMEMO_ERR_NOMEM; 189 | goto cleanup; 190 | } 191 | 192 | ret_val = gcry_cipher_decrypt(cipher_hd, out_p, ciphertext_len, ciphertext_p, ciphertext_len); 193 | if (ret_val) { 194 | ret_val = -ret_val; 195 | goto cleanup; 196 | } 197 | 198 | ret_val = gcry_cipher_checktag(cipher_hd, tag_p, tag_len); 199 | if (ret_val) { 200 | ret_val = OMEMO_ERR_AUTH_FAIL; 201 | goto cleanup; 202 | } 203 | 204 | *plaintext_pp = out_p; 205 | *plaintext_len_p = ciphertext_len; 206 | 207 | cleanup: 208 | if (ret_val) { 209 | free(out_p); 210 | } 211 | 212 | if (hd_is_init) { 213 | gcry_cipher_close(cipher_hd); 214 | } 215 | 216 | return ret_val; 217 | } 218 | 219 | 220 | void omemo_default_crypto_teardown(void) { 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/libomemo_crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** 6 | * If these default implementations are used, OpenSSL needs to be initialised. 7 | * In case it is used for providing axolotl crypto, the init functions must NOT be called. 8 | */ 9 | void omemo_default_crypto_init(void); 10 | 11 | int omemo_default_crypto_random_bytes(uint8_t ** buf_pp, size_t buf_len, void * user_data_p); 12 | 13 | int omemo_default_crypto_aes_gcm_encrypt( const uint8_t * plaintext_p, size_t plaintext_len, 14 | const uint8_t * iv_p, size_t iv_len, 15 | const uint8_t * key_p, size_t key_len, 16 | size_t tag_len, 17 | void * user_data_p, 18 | uint8_t ** ciphertext_pp, size_t * ciphertext_len_p, 19 | uint8_t ** tag_pp); 20 | 21 | int omemo_default_crypto_aes_gcm_decrypt( const uint8_t * ciphertext_p, size_t ciphertext_len, 22 | const uint8_t * iv_p, size_t iv_len, 23 | const uint8_t * key_p, size_t key_len, 24 | uint8_t * tag_p, size_t tag_len, 25 | void * user_data_p, 26 | uint8_t ** plaintext_pp, size_t * plaintext_len_p); 27 | 28 | void omemo_default_crypto_teardown(void); 29 | -------------------------------------------------------------------------------- /src/libomemo_storage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "libomemo.h" 8 | 9 | #define xstr(s) str(s) 10 | #define str(s) #s 11 | 12 | #define DEVICELIST_TABLE_NAME "devicelists" 13 | #define DEVICELIST_NAME_NAME "name" 14 | #define DEVICELIST_ID_NAME "id" 15 | #define DEVICELIST_ADDED_NAME "date_added" 16 | #define DEVICELIST_LASTUSE_NAME "date_lastuse" 17 | #define DEVICELIST_TRUST_STATUS_NAME "trust_status" 18 | 19 | #define CHATLIST_TABLE_NAME "cl" 20 | #define CHATLIST_CHAT_NAME_NAME "chat_name" 21 | 22 | #define LURCH_TRUST_NONE 0 23 | 24 | #define STMT_PROLOG "BEGIN TRANSACTION;"\ 25 | "CREATE TABLE IF NOT EXISTS " DEVICELIST_TABLE_NAME "("\ 26 | DEVICELIST_NAME_NAME " TEXT NOT NULL, "\ 27 | DEVICELIST_ID_NAME " INTEGER NOT NULL, "\ 28 | DEVICELIST_ADDED_NAME " TEXT NOT NULL, "\ 29 | DEVICELIST_LASTUSE_NAME " TEXT NOT NULL, "\ 30 | DEVICELIST_TRUST_STATUS_NAME " INTEGER NOT NULL, "\ 31 | "PRIMARY KEY("DEVICELIST_NAME_NAME", "DEVICELIST_ID_NAME"));"\ 32 | "CREATE TABLE IF NOT EXISTS " CHATLIST_TABLE_NAME " ("\ 33 | CHATLIST_CHAT_NAME_NAME " TEXT PRIMARY KEY);" 34 | 35 | #define STMT_EPILOG "COMMIT TRANSACTION;" 36 | 37 | 38 | static int db_conn_open_and_prepare(sqlite3 ** db_pp, sqlite3_stmt ** pstmt_pp, const char * stmt, const char * db_fn) { 39 | int ret_val = 0; 40 | 41 | sqlite3 * db_p = (void *) 0; 42 | sqlite3_stmt * pstmt_p = (void *) 0; 43 | char * err_msg = (void *) 0; 44 | 45 | 46 | ret_val = sqlite3_open(db_fn, &db_p); 47 | if (ret_val) { 48 | ret_val = -ret_val; 49 | goto cleanup; 50 | } 51 | 52 | (void) sqlite3_exec(db_p, STMT_PROLOG, (void *) 0, (void *) 0, &err_msg); 53 | if (err_msg) { 54 | ret_val = OMEMO_ERR_STORAGE; 55 | goto cleanup; 56 | } 57 | 58 | ret_val = sqlite3_prepare_v2(db_p, stmt, -1, &pstmt_p, (void *) 0); 59 | if (ret_val) { 60 | ret_val = -ret_val; 61 | goto cleanup; 62 | } 63 | 64 | *db_pp = db_p; 65 | *pstmt_pp = pstmt_p; 66 | 67 | cleanup: 68 | if (ret_val) { 69 | sqlite3_finalize(pstmt_p); 70 | sqlite3_close(db_p); 71 | sqlite3_free(err_msg); 72 | } 73 | 74 | return ret_val; 75 | } 76 | 77 | static int db_conn_commit(sqlite3 * db_p) { 78 | if (!db_p) { 79 | return OMEMO_ERR_NULL; 80 | } 81 | 82 | char * err_msg = (void *) 0; 83 | (void) sqlite3_exec(db_p, STMT_EPILOG, (void *) 0, (void *) 0, &err_msg); 84 | if (err_msg) { 85 | sqlite3_free(err_msg); 86 | return OMEMO_ERR_STORAGE; 87 | } 88 | 89 | return 0; 90 | } 91 | 92 | int omemo_storage_user_device_id_save(const char * user, uint32_t device_id, const char * db_fn) { 93 | const char * stmt = "INSERT INTO " DEVICELIST_TABLE_NAME " VALUES(" 94 | "?1, " 95 | "?2, " 96 | "datetime('now'), " 97 | "datetime('now'), " 98 | xstr(LURCH_TRUST_NONE) 99 | ");"; 100 | 101 | int ret_val = 0; 102 | 103 | sqlite3 * db_p = (void *) 0; 104 | sqlite3_stmt * pstmt_p = (void *) 0; 105 | 106 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 107 | if (ret_val) { 108 | goto cleanup; 109 | } 110 | 111 | ret_val = sqlite3_bind_text(pstmt_p, 1, user, -1, SQLITE_STATIC); 112 | if (ret_val) { 113 | ret_val = -ret_val; 114 | goto cleanup; 115 | } 116 | 117 | ret_val = sqlite3_bind_int(pstmt_p, 2, device_id); 118 | if (ret_val) { 119 | ret_val = -ret_val; 120 | goto cleanup; 121 | } 122 | 123 | ret_val = sqlite3_step(pstmt_p); 124 | if (ret_val != SQLITE_DONE) { 125 | ret_val = -ret_val; 126 | goto cleanup; 127 | } 128 | 129 | ret_val = db_conn_commit(db_p); 130 | if (ret_val) { 131 | goto cleanup; 132 | } 133 | 134 | 135 | cleanup: 136 | sqlite3_finalize(pstmt_p); 137 | sqlite3_close(db_p); 138 | 139 | return ret_val; 140 | } 141 | 142 | int omemo_storage_user_device_id_delete(const char * user, uint32_t device_id, const char * db_fn) { 143 | const char * stmt = "DELETE FROM " DEVICELIST_TABLE_NAME 144 | " WHERE " DEVICELIST_NAME_NAME " IS ?1" 145 | " AND " DEVICELIST_ID_NAME " IS ?2;"; 146 | 147 | int ret_val = 0; 148 | 149 | sqlite3 * db_p = (void *) 0; 150 | sqlite3_stmt * pstmt_p = (void *) 0; 151 | 152 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 153 | if (ret_val) { 154 | goto cleanup; 155 | } 156 | 157 | ret_val = sqlite3_bind_text(pstmt_p, 1, user, -1, SQLITE_STATIC); 158 | if (ret_val) { 159 | ret_val = -ret_val; 160 | goto cleanup; 161 | } 162 | 163 | ret_val = sqlite3_bind_int(pstmt_p, 2, device_id); 164 | if (ret_val) { 165 | ret_val = -ret_val; 166 | goto cleanup; 167 | } 168 | 169 | ret_val = sqlite3_step(pstmt_p); 170 | if (ret_val != SQLITE_DONE) { 171 | ret_val = -ret_val; 172 | goto cleanup; 173 | } 174 | 175 | ret_val = db_conn_commit(db_p); 176 | if (ret_val) { 177 | goto cleanup; 178 | } 179 | 180 | cleanup: 181 | sqlite3_finalize(pstmt_p); 182 | sqlite3_close(db_p); 183 | 184 | return ret_val; 185 | } 186 | 187 | int omemo_storage_user_devicelist_retrieve(const char * user, const char * db_fn, omemo_devicelist ** dl_pp) { 188 | const char * stmt = "SELECT * FROM " DEVICELIST_TABLE_NAME " WHERE name IS ?1;"; 189 | 190 | int ret_val = 0; 191 | 192 | omemo_devicelist * dl_p = (void *) 0; 193 | sqlite3 * db_p = (void *) 0; 194 | sqlite3_stmt * pstmt_p = (void *) 0; 195 | 196 | ret_val = omemo_devicelist_create(user, &dl_p); 197 | if (ret_val) { 198 | goto cleanup; 199 | } 200 | 201 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 202 | if (ret_val) { 203 | goto cleanup; 204 | } 205 | 206 | ret_val = sqlite3_bind_text(pstmt_p, 1, user, -1, SQLITE_STATIC); 207 | if (ret_val) { 208 | ret_val = -ret_val; 209 | goto cleanup; 210 | } 211 | 212 | ret_val = sqlite3_step(pstmt_p); 213 | while (ret_val == SQLITE_ROW) { 214 | ret_val = omemo_devicelist_add(dl_p, sqlite3_column_int(pstmt_p, 1)); 215 | if (ret_val) { 216 | goto cleanup; 217 | } 218 | 219 | ret_val = sqlite3_step(pstmt_p); 220 | } 221 | 222 | ret_val = db_conn_commit(db_p); 223 | if (ret_val) { 224 | goto cleanup; 225 | } 226 | 227 | *dl_pp = dl_p; 228 | 229 | cleanup: 230 | if (ret_val) { 231 | omemo_devicelist_destroy(dl_p); 232 | } 233 | sqlite3_finalize(pstmt_p); 234 | sqlite3_close(db_p); 235 | 236 | return ret_val; 237 | } 238 | 239 | int omemo_storage_chatlist_save(const char * chat, const char * db_fn) { 240 | const char * stmt = "INSERT OR REPLACE INTO " CHATLIST_TABLE_NAME " VALUES(?1);"; 241 | 242 | int ret_val = 0; 243 | 244 | sqlite3 * db_p = (void *) 0; 245 | sqlite3_stmt * pstmt_p = (void *) 0; 246 | 247 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 248 | if (ret_val) { 249 | goto cleanup; 250 | } 251 | 252 | ret_val = sqlite3_bind_text(pstmt_p, 1, chat, -1, SQLITE_STATIC); 253 | if (ret_val) { 254 | ret_val = -ret_val; 255 | goto cleanup; 256 | } 257 | 258 | ret_val = sqlite3_step(pstmt_p); 259 | if (ret_val != SQLITE_DONE) { 260 | ret_val = -ret_val; 261 | goto cleanup; 262 | } 263 | 264 | ret_val = db_conn_commit(db_p); 265 | if (ret_val) { 266 | goto cleanup; 267 | } 268 | 269 | 270 | cleanup: 271 | sqlite3_finalize(pstmt_p); 272 | sqlite3_close(db_p); 273 | 274 | return ret_val; 275 | } 276 | 277 | int omemo_storage_chatlist_exists(const char * chat, const char * db_fn) { 278 | const char * stmt = "SELECT " CHATLIST_CHAT_NAME_NAME " FROM " CHATLIST_TABLE_NAME 279 | " WHERE " CHATLIST_CHAT_NAME_NAME " IS ?1;"; 280 | 281 | int ret_val = 0; 282 | 283 | sqlite3 * db_p = (void *) 0; 284 | sqlite3_stmt * pstmt_p = (void *) 0; 285 | 286 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 287 | if (ret_val) { 288 | goto cleanup; 289 | } 290 | 291 | ret_val = sqlite3_bind_text(pstmt_p, 1, chat, -1, SQLITE_STATIC); 292 | if (ret_val) { 293 | ret_val = -ret_val; 294 | goto cleanup; 295 | } 296 | 297 | ret_val = sqlite3_step(pstmt_p); 298 | if (ret_val != SQLITE_ROW) { 299 | ret_val = (ret_val == SQLITE_DONE) ? 0 : -ret_val; 300 | } else { 301 | ret_val = 1; 302 | } 303 | 304 | cleanup: 305 | sqlite3_finalize(pstmt_p); 306 | sqlite3_close(db_p); 307 | 308 | return ret_val; 309 | } 310 | 311 | int omemo_storage_chatlist_delete(const char * chat, const char * db_fn) { 312 | const char * stmt = "DELETE FROM " CHATLIST_TABLE_NAME " WHERE " CHATLIST_CHAT_NAME_NAME " IS ?1;"; 313 | 314 | int ret_val = 0; 315 | 316 | sqlite3 * db_p = (void *) 0; 317 | sqlite3_stmt * pstmt_p = (void *) 0; 318 | 319 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 320 | if (ret_val) { 321 | goto cleanup; 322 | } 323 | 324 | ret_val = sqlite3_bind_text(pstmt_p, 1, chat, -1, SQLITE_STATIC); 325 | if (ret_val) { 326 | ret_val = -ret_val; 327 | goto cleanup; 328 | } 329 | 330 | ret_val = sqlite3_step(pstmt_p); 331 | if (ret_val != SQLITE_DONE) { 332 | ret_val = -ret_val; 333 | goto cleanup; 334 | } 335 | 336 | ret_val = db_conn_commit(db_p); 337 | if (ret_val) { 338 | goto cleanup; 339 | } 340 | 341 | cleanup: 342 | sqlite3_finalize(pstmt_p); 343 | sqlite3_close(db_p); 344 | 345 | return ret_val; 346 | } 347 | 348 | 349 | int omemo_storage_global_device_id_exists(uint32_t device_id, const char * db_fn) { 350 | const char * stmt = "SELECT " DEVICELIST_ID_NAME " FROM " DEVICELIST_TABLE_NAME 351 | " WHERE " DEVICELIST_ID_NAME " IS ?1;"; 352 | 353 | int ret_val = 0; 354 | 355 | sqlite3 * db_p = (void *) 0; 356 | sqlite3_stmt * pstmt_p = (void *) 0; 357 | 358 | ret_val = db_conn_open_and_prepare(&db_p, &pstmt_p, stmt, db_fn); 359 | if (ret_val) { 360 | goto cleanup; 361 | } 362 | 363 | ret_val = sqlite3_bind_int(pstmt_p, 1, device_id); 364 | if (ret_val) { 365 | ret_val = -ret_val; 366 | goto cleanup; 367 | } 368 | 369 | ret_val = sqlite3_step(pstmt_p); 370 | if (ret_val != SQLITE_ROW) { 371 | ret_val = (ret_val == SQLITE_DONE) ? 0 : -ret_val; 372 | } else { 373 | ret_val = 1; 374 | } 375 | 376 | cleanup: 377 | sqlite3_finalize(pstmt_p); 378 | sqlite3_close(db_p); 379 | 380 | return ret_val; 381 | } 382 | -------------------------------------------------------------------------------- /src/libomemo_storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "libomemo.h" 6 | 7 | /* 8 | * Saves a device ID for a username. 9 | * 10 | * @param user Owner of the devicelist. 11 | * @param device_id The device ID. 12 | * @param db_fn Path to the DB. 13 | * @return 0 on success, negative on error (e.g. negated SQLite3 error codes). 14 | */ 15 | int omemo_storage_user_device_id_save(const char * user, uint32_t device_id, const char * db_fn); 16 | 17 | /** 18 | * Deletes a device ID for a username. 19 | * 20 | * @param user Owner of the devicelist. 21 | * @param device_id The device ID. 22 | * @param db_fn Path to the DB. 23 | * @return 0 on success, negative on error. 24 | */ 25 | int omemo_storage_user_device_id_delete(const char * user, uint32_t device_id, const char * db_fn); 26 | 27 | /** 28 | * Retrieves the list of devices for a user. 29 | * If the function returns success but the ID list inside the devicelist is empty, there are no entries. 30 | * 31 | * @param user User to look for. 32 | * @param db_fn Path to the DB. 33 | * @param dl_pp Will be set to the devicelist. 34 | * @return 0 on success, negative on error (e.g. negated SQLite3 error codes). 35 | */ 36 | int omemo_storage_user_devicelist_retrieve(const char * user, const char * db_fn, omemo_devicelist ** dl_pp); 37 | 38 | /** 39 | * Saves a chat to "the list" (used as whitelist for groupchats, and blacklist for normal chats). 40 | * 41 | * @param chat The name of the chat. 42 | * @param db_fn Path to the DB. 43 | * @return 0 on success, negative on error 44 | */ 45 | int omemo_storage_chatlist_save(const char * chat, const char * db_fn); 46 | 47 | /** 48 | * Looks up a chat in "the list". 49 | * 50 | * @param chat The name of the chat. 51 | * @param db_fn Path to the DB. 52 | * @return 1 if it exists, 0 if it does not, negative on error. 53 | */ 54 | int omemo_storage_chatlist_exists(const char * chat, const char * db_fn); 55 | 56 | /** 57 | * Deletes a chat from "the list". 58 | * 59 | * @param chat The name of the chat. 60 | * @param db_fn Path to the DB. 61 | * @return 0 on success, negative on error. 62 | */ 63 | int omemo_storage_chatlist_delete(const char * chat, const char * db_fn); 64 | 65 | /** 66 | * Checks if the device ID is contained in the database. 67 | * 68 | * @param device_id The device ID to look for. 69 | * @param db_fn Path to the omemo DB. 70 | * @return 1 if true, 0 if false, negative on error 71 | */ 72 | int omemo_storage_global_device_id_exists(uint32_t device_id, const char * db_fn); 73 | -------------------------------------------------------------------------------- /test/test_crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../src/libomemo_crypto.c" 7 | 8 | int openssl_init(void ** state) { 9 | (void) state; 10 | 11 | omemo_default_crypto_init(); 12 | 13 | return 0; 14 | } 15 | 16 | int openssl_teardown(void ** state) { 17 | (void) state; 18 | 19 | omemo_default_crypto_teardown(); 20 | 21 | return 0; 22 | } 23 | 24 | void test_random_bytes(void ** state) { 25 | (void) state; 26 | 27 | assert_int_not_equal(omemo_default_crypto_random_bytes((void *) 0, 0, (void *) 0), 0); 28 | 29 | uint8_t * buf = (void *) 0; 30 | assert_int_equal(omemo_default_crypto_random_bytes(&buf, 4, (void *) 0), 0); 31 | free(buf); 32 | } 33 | 34 | void test_aes_gcm_encrypt_decrypt(void ** state) { 35 | (void) state; 36 | char * msg = "hello aes"; 37 | uint8_t * plaintext_p = (uint8_t *) msg; 38 | size_t plaintext_len = strlen(msg) + 1; 39 | 40 | size_t iv_len = OMEMO_AES_GCM_IV_LENGTH; 41 | uint8_t * iv_p = (void *) 0; 42 | assert_int_equal(omemo_default_crypto_random_bytes(&iv_p, iv_len, (void *) 0), 0); 43 | 44 | size_t key_len = OMEMO_AES_128_KEY_LENGTH; 45 | uint8_t * key_p = (void *) 0; 46 | assert_int_equal(omemo_default_crypto_random_bytes(&key_p, key_len, (void *) 0), 0); 47 | 48 | size_t tag_len = OMEMO_AES_GCM_TAG_LENGTH; 49 | 50 | uint8_t * ciphertext_p = (void *) 0; 51 | size_t ciphertext_len = 0; 52 | 53 | uint8_t * tag_p = (void *) 0; 54 | 55 | assert_int_equal(omemo_default_crypto_aes_gcm_encrypt(plaintext_p, plaintext_len, 56 | iv_p, iv_len, 57 | key_p, key_len, 58 | tag_len, 59 | (void *) 0, 60 | &ciphertext_p, &ciphertext_len, 61 | &tag_p), 0); 62 | 63 | 64 | uint8_t * result_p = (void *) 0; 65 | size_t result_len = 0; 66 | 67 | assert_int_equal(omemo_default_crypto_aes_gcm_decrypt(ciphertext_p, ciphertext_len, 68 | iv_p, iv_len, 69 | key_p, key_len, 70 | tag_p, tag_len, 71 | (void *) 0, 72 | &result_p, &result_len), 0); 73 | 74 | assert_int_equal(plaintext_len, result_len); 75 | assert_memory_equal(plaintext_p, result_p, plaintext_len); 76 | 77 | // now change the ct so that the tag verification fails 78 | ciphertext_p[0] += 1; 79 | assert_int_equal(omemo_default_crypto_aes_gcm_decrypt(ciphertext_p, ciphertext_len, 80 | iv_p, iv_len, 81 | key_p, key_len, 82 | tag_p, tag_len, 83 | (void *) 0, 84 | &result_p, &result_len), OMEMO_ERR_AUTH_FAIL); 85 | 86 | free(result_p); 87 | free(tag_p); 88 | free(ciphertext_p); 89 | free(key_p); 90 | free(iv_p); 91 | } 92 | 93 | int main(void) { 94 | const struct CMUnitTest tests[] = { 95 | cmocka_unit_test(test_random_bytes), 96 | cmocka_unit_test(test_aes_gcm_encrypt_decrypt) 97 | }; 98 | 99 | return cmocka_run_group_tests_name("omemo default crypto", tests, openssl_init, openssl_teardown); 100 | } 101 | -------------------------------------------------------------------------------- /test/test_libomemo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../src/libomemo.c" 7 | #include "../src/libomemo_crypto.c" 8 | 9 | char * devicelist = "" 10 | "" 11 | "" 12 | "" 13 | "" 14 | "" 15 | "" 16 | ""; 17 | 18 | char * bundle = "" 19 | "" 20 | "" 21 | "sWsAtQ==" 22 | "sWsAtQ==" 23 | "sWsAtQ==" 24 | "" 25 | "sWsAtQ==" 26 | "sWsAtQ==" 27 | "sWsAtQ==" 28 | "sWsAtQ==" 29 | "" 30 | "" 31 | "" 32 | ""; 33 | 34 | char * msg_out = "" 35 | "hello" 36 | ""; 37 | 38 | char * msg_out_extra = "" 39 | "" 40 | "hello" 41 | ""; 42 | 43 | uint8_t data[] = {0xB1, 0x6B, 0x00, 0xB5}; 44 | char * data_b64 = "sWsAtQ=="; 45 | 46 | omemo_crypto_provider crypto = { 47 | .random_bytes_func = omemo_default_crypto_random_bytes, 48 | .aes_gcm_encrypt_func = omemo_default_crypto_aes_gcm_encrypt, 49 | .aes_gcm_decrypt_func = omemo_default_crypto_aes_gcm_decrypt, 50 | (void *) 0 51 | }; 52 | 53 | void test_devicelist_create(void ** state) { 54 | (void) state; 55 | 56 | assert_int_equal(omemo_devicelist_create((void *) 0, (void *) 0), OMEMO_ERR_NULL); 57 | assert_int_equal(omemo_devicelist_create("test", (void *) 0), OMEMO_ERR_NULL); 58 | 59 | omemo_devicelist * dl_p; 60 | assert_int_equal(omemo_devicelist_create("alice", &dl_p), 0); 61 | assert_string_equal(dl_p->from, "alice"); 62 | assert_string_equal(mxmlGetElement(dl_p->list_node_p), "list"); 63 | assert_string_equal(mxmlElementGetAttr(dl_p->list_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 64 | assert_ptr_equal(mxmlGetFirstChild(dl_p->list_node_p), (void *) 0); 65 | 66 | omemo_devicelist_destroy(dl_p); 67 | } 68 | 69 | void test_devicelist_import(void ** state) { 70 | (void) state; 71 | 72 | omemo_devicelist * dl_p; 73 | assert_int_equal(omemo_devicelist_import(devicelist, "bob", &dl_p), 0); 74 | assert_string_equal(dl_p->from, "bob"); 75 | assert_string_equal(mxmlGetElement(dl_p->list_node_p), "list"); 76 | assert_string_equal(mxmlElementGetAttr(dl_p->list_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 77 | 78 | mxml_node_t * device_node_p; 79 | assert_int_equal(expect_next_node(dl_p->list_node_p, mxmlGetFirstChild, "device", &device_node_p), 0); 80 | assert_string_equal(mxmlElementGetAttr(device_node_p, "id"), "4223"); 81 | 82 | assert_int_equal(expect_next_node(device_node_p, mxmlGetNextSibling, "device", &device_node_p), 0); 83 | assert_string_equal(mxmlElementGetAttr(device_node_p, "id"), "1337"); 84 | 85 | assert_ptr_equal(mxmlGetNextSibling(device_node_p), (void *) 0); 86 | assert_ptr_equal(mxmlGetNextSibling(dl_p->list_node_p), (void *) 0); 87 | 88 | uint32_t * id_p = dl_p->id_list_p->data; 89 | assert_int_equal(*id_p, 4223); 90 | 91 | id_p = dl_p->id_list_p->next->data; 92 | assert_int_equal(*id_p, 1337); 93 | 94 | assert_ptr_equal(dl_p->id_list_p->next->next, (void *) 0); 95 | 96 | omemo_devicelist_destroy(dl_p); 97 | } 98 | 99 | void test_devicelist_import_empty(void ** state) { 100 | (void) state; 101 | 102 | char * devicelist_empty = "" 103 | "" 104 | "" 105 | "" 106 | "" 107 | ""; 108 | 109 | omemo_devicelist * dl_p; 110 | assert_int_equal(omemo_devicelist_import(devicelist_empty, "alice", &dl_p), 0); 111 | 112 | assert_int_equal(omemo_devicelist_is_empty(dl_p), 1); 113 | 114 | omemo_devicelist_destroy(dl_p); 115 | } 116 | 117 | void test_devicelist_import_empty_alt(void ** state) { 118 | (void) state; 119 | 120 | char * devicelist_empty = ""; 121 | 122 | omemo_devicelist * dl_p; 123 | assert_int_equal(omemo_devicelist_import(devicelist_empty, "alice", &dl_p), 0); 124 | 125 | assert_int_equal(omemo_devicelist_is_empty(dl_p), 1); 126 | 127 | omemo_devicelist_destroy(dl_p); 128 | } 129 | 130 | void test_devicelist_add(void ** state) { 131 | (void) state; 132 | 133 | omemo_devicelist * dl_p; 134 | assert_int_equal(omemo_devicelist_create("alice", &dl_p), 0); 135 | assert_ptr_equal(omemo_devicelist_get_id_list(dl_p), (void *) 0); 136 | assert_int_equal(omemo_devicelist_add(dl_p, 123456), 0); 137 | 138 | mxml_node_t * device_node_p = mxmlGetFirstChild(dl_p->list_node_p); 139 | assert_string_equal(mxmlGetElement(device_node_p), "device"); 140 | assert_string_equal(mxmlElementGetAttr(device_node_p, "id"), "123456"); 141 | assert_ptr_equal(mxmlGetNextSibling(device_node_p), (void *) 0); 142 | 143 | uint32_t * id_p = dl_p->id_list_p->data; 144 | assert_int_equal(*id_p, 123456); 145 | assert_ptr_equal(dl_p->id_list_p->next, (void *) 0); 146 | 147 | GList * dl_l_p = omemo_devicelist_get_id_list(dl_p); 148 | assert_ptr_not_equal(dl_l_p, (void *) 0); 149 | assert_int_equal(omemo_devicelist_list_data(dl_l_p), 123456); 150 | assert_ptr_equal(dl_l_p->next, (void *) 0); 151 | 152 | g_list_free_full(dl_l_p, free); 153 | omemo_devicelist_destroy(dl_p); 154 | } 155 | 156 | void test_devicelist_contains_id(void ** state) { 157 | (void) state; 158 | 159 | omemo_devicelist * dl_p; 160 | assert_int_equal(omemo_devicelist_create("alice", &dl_p), 0); 161 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 123456), 0); 162 | assert_int_equal(omemo_devicelist_add(dl_p, 123456), 0); 163 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 123456), 1); 164 | omemo_devicelist_destroy(dl_p); 165 | 166 | assert_int_equal(omemo_devicelist_import(devicelist, "bob", &dl_p), 0); 167 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 123456), 0); 168 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1337), 1); 169 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 4223), 1); 170 | assert_int_equal(omemo_devicelist_add(dl_p, 123456), 0); 171 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 123456), 1); 172 | omemo_devicelist_destroy(dl_p); 173 | } 174 | 175 | void test_devicelist_remove(void ** state) { 176 | (void) state; 177 | 178 | omemo_devicelist * dl_p; 179 | assert_int_equal(omemo_devicelist_import(devicelist, "alice", &dl_p), 0); 180 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1337), 1); 181 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 4223), 1); 182 | 183 | assert_int_equal(omemo_devicelist_remove(dl_p, 1337), 0); 184 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1337), 0); 185 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 4223), 1); 186 | 187 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1338), 0); 188 | assert_int_equal(omemo_devicelist_remove(dl_p, 1338), 0); 189 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1338), 0); 190 | 191 | assert_int_equal(omemo_devicelist_remove(dl_p, 4223), 0); 192 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1337), 0); 193 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 4223), 0); 194 | 195 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1338), 0); 196 | assert_int_equal(omemo_devicelist_remove(dl_p, 1338), 0); 197 | assert_int_equal(omemo_devicelist_contains_id(dl_p, 1338), 0); 198 | 199 | omemo_devicelist_destroy(dl_p); 200 | } 201 | 202 | void test_devicelist_diff(void ** state) { 203 | (void) state; 204 | 205 | omemo_devicelist * dl_a_p, * dl_b_p; 206 | assert_int_equal(omemo_devicelist_create("alice", &dl_a_p), 0); 207 | assert_int_equal(omemo_devicelist_create("alice", &dl_b_p), 0); 208 | 209 | GList * amb_p, * bma_p; 210 | assert_int_equal(omemo_devicelist_diff((void *) 0, (void *) 0, (void *) 0, (void *) 0), OMEMO_ERR_NULL); 211 | assert_int_equal(omemo_devicelist_diff(dl_a_p, (void *) 0, (void *) 0, (void *) 0), OMEMO_ERR_NULL); 212 | assert_int_equal(omemo_devicelist_diff(dl_a_p, dl_b_p, (void *) 0, (void *) 0), OMEMO_ERR_NULL); 213 | assert_int_equal(omemo_devicelist_diff(dl_a_p, dl_b_p, &amb_p, (void *) 0), OMEMO_ERR_NULL); 214 | 215 | assert_int_equal(omemo_devicelist_diff(dl_a_p, dl_b_p, &amb_p, &bma_p), 0); 216 | assert_ptr_equal(amb_p, (void *) 0); 217 | assert_ptr_equal(bma_p, (void *) 0); 218 | 219 | assert_int_equal(omemo_devicelist_add(dl_a_p, 1111), 0); 220 | assert_int_equal(omemo_devicelist_diff(dl_a_p, dl_b_p, &amb_p, &bma_p), 0); 221 | assert_ptr_equal(bma_p, (void *) 0); 222 | assert_int_equal(g_list_length(amb_p), 1); 223 | assert_int_equal(omemo_devicelist_list_data(amb_p), 1111); 224 | g_list_free_full(amb_p, free); 225 | g_list_free_full(bma_p, free); 226 | 227 | assert_int_equal(omemo_devicelist_add(dl_b_p, 1111), 0); 228 | assert_int_equal(omemo_devicelist_add(dl_b_p, 2222), 0); 229 | assert_int_equal(omemo_devicelist_diff(dl_a_p, dl_b_p, &amb_p, &bma_p), 0); 230 | assert_ptr_equal(amb_p, (void *) 0); 231 | assert_int_equal(g_list_length(bma_p), 1); 232 | assert_int_equal(omemo_devicelist_list_data(bma_p), 2222); 233 | g_list_free_full(amb_p, free); 234 | g_list_free_full(bma_p, free); 235 | 236 | omemo_devicelist_destroy(dl_a_p); 237 | omemo_devicelist_destroy(dl_b_p); 238 | } 239 | 240 | void test_devicelist_export(void ** state) { 241 | (void) state; 242 | 243 | omemo_devicelist * dl_p; 244 | assert_int_equal(omemo_devicelist_create("alice", &dl_p), 0); 245 | assert_int_equal(omemo_devicelist_add(dl_p, 54321), 0); 246 | assert_int_equal(omemo_devicelist_add(dl_p, 56789), 0); 247 | 248 | char * xml; 249 | assert_int_equal(omemo_devicelist_export(dl_p, &xml), 0); 250 | 251 | mxml_node_t * publish_node_p = mxmlLoadString((void *) 0, xml, MXML_NO_CALLBACK); 252 | assert_ptr_not_equal(publish_node_p, (void *) 0); 253 | assert_string_equal(mxmlGetElement(publish_node_p), "publish"); 254 | assert_string_equal(mxmlElementGetAttr(publish_node_p, "node"), "eu.siacs.conversations.axolotl.devicelist"); 255 | 256 | mxml_node_t * item_node_p = mxmlGetFirstChild(publish_node_p); 257 | assert_string_equal(mxmlGetElement(item_node_p), "item"); 258 | 259 | mxml_node_t * list_node_p = mxmlGetFirstChild(item_node_p); 260 | assert_string_equal(mxmlGetElement(list_node_p), "list"); 261 | assert_string_equal(mxmlElementGetAttr(list_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 262 | 263 | mxml_node_t * device_node_p = mxmlGetFirstChild(list_node_p); 264 | assert_string_equal(mxmlGetElement(device_node_p), "device"); 265 | assert_string_equal(mxmlElementGetAttr(device_node_p, "id"), "54321"); 266 | 267 | device_node_p = mxmlGetNextSibling(device_node_p); 268 | assert_string_equal(mxmlGetElement(device_node_p), "device"); 269 | assert_string_equal(mxmlElementGetAttr(device_node_p, "id"), "56789"); 270 | 271 | mxmlDelete(publish_node_p); 272 | free(xml); 273 | omemo_devicelist_destroy(dl_p); 274 | } 275 | 276 | void test_devicelist_get_pep_node_name(void ** state) { 277 | (void) state; 278 | 279 | char * node_name = (void *) 0; 280 | assert_int_equal(omemo_devicelist_get_pep_node_name(&node_name), 0); 281 | assert_string_equal(node_name, "eu.siacs.conversations.axolotl.devicelist"); 282 | 283 | free(node_name); 284 | } 285 | 286 | void test_devicelist_get_id_list(void ** state) { 287 | (void) state; 288 | 289 | omemo_devicelist * dl_p; 290 | assert_int_equal(omemo_devicelist_import(devicelist, "alice", &dl_p), 0); 291 | 292 | GList * dl_l_p = omemo_devicelist_get_id_list(dl_p); 293 | assert_ptr_not_equal(dl_l_p, (void *) 0); 294 | assert_int_equal(4223, omemo_devicelist_list_data(dl_l_p)); 295 | 296 | dl_l_p = dl_l_p->next; 297 | assert_int_equal(1337, omemo_devicelist_list_data(dl_l_p)); 298 | assert_ptr_equal(dl_l_p->next, (void *) 0); 299 | 300 | g_list_free_full(dl_l_p->prev, free); 301 | omemo_devicelist_destroy(dl_p); 302 | } 303 | 304 | void test_bundle_create(void ** state) { 305 | (void) state; 306 | 307 | omemo_bundle * bundle_p = (void *) 0; 308 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 309 | 310 | assert_ptr_not_equal(bundle_p, (void *) 0); 311 | 312 | assert_int_equal(bundle_p->pre_keys_amount, 0); 313 | 314 | omemo_bundle_destroy(bundle_p); 315 | } 316 | 317 | void test_bundle_set_device_id(void ** state) { 318 | (void) state; 319 | 320 | omemo_bundle * bundle_p = (void *) 0; 321 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 322 | 323 | assert_int_equal(omemo_bundle_set_device_id(bundle_p, 1337), 0); 324 | assert_string_equal(bundle_p->device_id, "1337"); 325 | } 326 | 327 | void test_bundle_set_signed_pre_key(void ** state) { 328 | (void) state; 329 | 330 | omemo_bundle * bundle_p = (void *) 0; 331 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 332 | 333 | uint8_t data[] = {0xB1, 0x6B, 0x00, 0xB5}; 334 | assert_int_equal(omemo_bundle_set_signed_pre_key(bundle_p, 1, &data[0], 4), 0); 335 | 336 | mxml_node_t * spkn_p = bundle_p->signed_pk_node_p; 337 | assert_ptr_not_equal(spkn_p, (void *) 0); 338 | assert_string_equal(mxmlElementGetAttr(spkn_p, SIGNED_PRE_KEY_NODE_ID_ATTR_NAME), "1"); 339 | assert_string_equal(mxmlGetOpaque(spkn_p), g_base64_encode(&data[0], 4)); 340 | 341 | omemo_bundle_destroy(bundle_p); 342 | } 343 | 344 | void test_bundle_set_signature(void ** state) { 345 | (void) state; 346 | 347 | omemo_bundle * bundle_p = (void *) 0; 348 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 349 | 350 | uint8_t data[] = {0xB1, 0x6B, 0x00, 0xB5}; 351 | assert_int_equal(omemo_bundle_set_signature(bundle_p, &data[0], 4), 0); 352 | 353 | mxml_node_t * signature_p = bundle_p->signature_node_p; 354 | assert_ptr_not_equal(signature_p, (void *) 0); 355 | assert_string_equal(mxmlGetOpaque(signature_p), g_base64_encode(&data[0], 4)); 356 | 357 | omemo_bundle_destroy(bundle_p); 358 | } 359 | 360 | void test_bundle_set_identity_key(void ** state) { 361 | (void) state; 362 | 363 | omemo_bundle * bundle_p = (void *) 0; 364 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 365 | 366 | uint8_t data[] = {0xB1, 0x6B, 0x00, 0xB5}; 367 | assert_int_equal(omemo_bundle_set_identity_key(bundle_p, &data[0], 4), 0); 368 | 369 | assert_ptr_not_equal(bundle_p->identity_key_node_p, (void *) 0); 370 | assert_string_equal(mxmlGetOpaque(bundle_p->identity_key_node_p), g_base64_encode(&data[0], 4)); 371 | 372 | omemo_bundle_destroy(bundle_p); 373 | } 374 | 375 | void test_bundle_add_pre_key(void ** state) { 376 | (void) state; 377 | 378 | omemo_bundle * bundle_p = (void *) 0; 379 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 380 | 381 | uint8_t data[] = {0xB1, 0x6B, 0x00, 0xB5}; 382 | assert_int_equal(omemo_bundle_add_pre_key(bundle_p, 1, &data[0], 4), 0); 383 | assert_ptr_not_equal(bundle_p->pre_keys_node_p, (void *) 0); 384 | assert_int_equal(bundle_p->pre_keys_amount, 1); 385 | assert_string_equal(mxmlGetElement(bundle_p->pre_keys_node_p), PREKEYS_NODE_NAME); 386 | 387 | mxml_node_t * pre_key_node_p = mxmlFindPath(bundle_p->pre_keys_node_p, PRE_KEY_NODE_NAME); 388 | pre_key_node_p = mxmlGetParent(pre_key_node_p); 389 | assert_ptr_not_equal(pre_key_node_p, (void *) 0); 390 | assert_string_equal(mxmlElementGetAttr(pre_key_node_p, PRE_KEY_NODE_ID_ATTR_NAME), "1"); 391 | assert_string_equal(mxmlGetOpaque(pre_key_node_p), g_base64_encode(&data[0], 4)); 392 | 393 | mxml_node_t * pk_node_before = bundle_p->pre_keys_node_p; 394 | uint8_t data2[] = {0xBA, 0xDF, 0xEE, 0x75}; 395 | assert_int_equal(omemo_bundle_add_pre_key(bundle_p, 2, &data2[0], 4), 0); 396 | assert_ptr_equal(pk_node_before, bundle_p->pre_keys_node_p); 397 | assert_int_equal(bundle_p->pre_keys_amount, 2); 398 | 399 | pre_key_node_p = mxmlGetNextSibling(pre_key_node_p); 400 | assert_ptr_not_equal(pre_key_node_p, (void *) 0); 401 | assert_string_equal(mxmlElementGetAttr(pre_key_node_p, PRE_KEY_NODE_ID_ATTR_NAME), "2"); 402 | assert_string_equal(mxmlGetOpaque(pre_key_node_p), g_base64_encode(&data2[0], 4)); 403 | 404 | omemo_bundle_destroy(bundle_p); 405 | } 406 | 407 | void test_bundle_export(void ** state) { 408 | (void) state; 409 | 410 | uint8_t * data_p = &data[0]; 411 | size_t len = 4; 412 | uint32_t reg_id = 1337; 413 | 414 | char * publish = (void *) 0; 415 | 416 | omemo_bundle * bundle_p = (void *) 0; 417 | assert_int_equal(omemo_bundle_create(&bundle_p), 0); 418 | assert_int_not_equal(omemo_bundle_export(bundle_p, &publish), 0); 419 | 420 | assert_int_equal(omemo_bundle_add_pre_key(bundle_p, 14444488, data_p, len), 0); 421 | assert_int_not_equal(omemo_bundle_export(bundle_p, &publish), 0); 422 | 423 | assert_int_equal(omemo_bundle_set_signature(bundle_p, data_p, len), 0); 424 | assert_int_not_equal(omemo_bundle_export(bundle_p, &publish), 0); 425 | 426 | assert_int_equal(omemo_bundle_set_signed_pre_key(bundle_p, 1, data_p, len), 0); 427 | assert_int_not_equal(omemo_bundle_export(bundle_p, &publish), 0); 428 | 429 | assert_int_equal(omemo_bundle_set_identity_key(bundle_p, data_p, len), 0); 430 | assert_int_not_equal(omemo_bundle_export(bundle_p, &publish), 0); 431 | 432 | assert_int_equal(omemo_bundle_set_device_id(bundle_p, reg_id), 0); 433 | 434 | for (int i = 0; i <= 20; i++) { 435 | assert_int_equal(omemo_bundle_add_pre_key(bundle_p, i, data_p, len), 0); 436 | } 437 | 438 | assert_int_equal(bundle_p->pre_keys_amount, 22); 439 | 440 | assert_int_equal(omemo_bundle_export(bundle_p, &publish), 0); 441 | assert_ptr_not_equal(publish, (void *) 0); 442 | 443 | mxml_node_t * publish_node_p = mxmlLoadString((void *) 0, publish, MXML_OPAQUE_CALLBACK); 444 | assert_ptr_not_equal(publish_node_p, (void *) 0); 445 | 446 | mxml_node_t * item_node_p = mxmlGetFirstChild(publish_node_p); 447 | assert_ptr_not_equal(item_node_p, (void *) 0); 448 | assert_string_equal(mxmlGetElement(item_node_p), "item"); 449 | 450 | mxml_node_t * bundle_node_p = mxmlGetFirstChild(item_node_p); 451 | assert_ptr_not_equal(bundle_node_p, (void *) 0); 452 | assert_string_equal(mxmlGetElement(bundle_node_p), "bundle"); 453 | assert_string_equal(mxmlElementGetAttr(bundle_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 454 | 455 | mxml_node_t * signed_pk_node_p = mxmlGetFirstChild(bundle_node_p); 456 | assert_ptr_not_equal(signed_pk_node_p, (void *) 0); 457 | assert_string_equal(mxmlGetElement(signed_pk_node_p), "signedPreKeyPublic"); 458 | assert_string_equal(mxmlElementGetAttr(signed_pk_node_p, "signedPreKeyId"), "1"); 459 | 460 | mxml_node_t * signature_node_p = mxmlGetNextSibling(signed_pk_node_p); 461 | assert_ptr_not_equal(signature_node_p, (void *) 0); 462 | assert_string_equal(mxmlGetElement(signature_node_p), "signedPreKeySignature"); 463 | 464 | mxml_node_t * identity_key_node_p = mxmlGetNextSibling(signature_node_p); 465 | assert_ptr_not_equal(identity_key_node_p, (void *) 0); 466 | assert_string_equal(mxmlGetElement(identity_key_node_p), "identityKey"); 467 | 468 | mxml_node_t * prekeys_node_p = mxmlGetNextSibling(identity_key_node_p); 469 | assert_ptr_not_equal(prekeys_node_p, (void *) 0); 470 | assert_string_equal(mxmlGetElement(prekeys_node_p), "prekeys"); 471 | 472 | mxml_node_t * pre_key_node_p = mxmlGetFirstChild(prekeys_node_p); 473 | assert_ptr_not_equal(pre_key_node_p, (void *) 0); 474 | assert_string_equal(mxmlGetElement(pre_key_node_p), "preKeyPublic"); 475 | assert_string_equal(mxmlElementGetAttr(pre_key_node_p, "preKeyId"), "14444488"); 476 | 477 | pre_key_node_p = mxmlGetLastChild(prekeys_node_p); 478 | assert_ptr_not_equal(pre_key_node_p, (void *) 0); 479 | assert_string_equal(mxmlGetElement(pre_key_node_p), "preKeyPublic"); 480 | assert_string_equal(mxmlElementGetAttr(pre_key_node_p, "preKeyId"), "20"); 481 | 482 | mxmlDelete(publish_node_p); 483 | } 484 | 485 | void test_bundle_get_pep_node_name(void ** state) { 486 | (void) state; 487 | 488 | char * node_name = (void *) 0; 489 | assert_int_equal(omemo_bundle_get_pep_node_name(1337, &node_name), 0); 490 | assert_string_equal(node_name, "eu.siacs.conversations.axolotl.bundles:1337"); 491 | 492 | free(node_name); 493 | } 494 | 495 | void test_bundle_import_malformed(void ** state) { 496 | (void) state; 497 | 498 | char * bundle_malformed_1 = "" 499 | "" 500 | "sWsAtQ==" 501 | "sWsAtQ==" 502 | "sWsAtQ==" 503 | "" 504 | "sWsAtQ==" 505 | "sWsAtQ==" 506 | "sWsAtQ==" 507 | "sWsAtQ==" 508 | "" 509 | "" 510 | "" 511 | ""; 512 | 513 | char * bundle_malformed_2 = "" 514 | "" 515 | "" 516 | "sWsAtQ==" 517 | "sWsAtQ==" 518 | "" 519 | "sWsAtQ==" 520 | "sWsAtQ==" 521 | "sWsAtQ==" 522 | "sWsAtQ==" 523 | "" 524 | "" 525 | "" 526 | ""; 527 | 528 | char * bundle_malformed_3 = "" 529 | "" 530 | "" 531 | "sWsAtQ==" 532 | "sWsAtQ==" 533 | "" 534 | "sWsAtQ==" 535 | "sWsAtQ==" 536 | "sWsAtQ==" 537 | "sWsAtQ==" 538 | "" 539 | "" 540 | "" 541 | ""; 542 | 543 | char * bundle_malformed_4 = "" 544 | "" 545 | "" 546 | "sWsAtQ==" 547 | "sWsAtQ==" 548 | "" 549 | "sWsAtQ==" 550 | "sWsAtQ==" 551 | "sWsAtQ==" 552 | "sWsAtQ==" 553 | "" 554 | "" 555 | "" 556 | ""; 557 | 558 | char * bundle_malformed_5 = "" 559 | "" 560 | "" 561 | "sWsAtQ==" 562 | "sWsAtQ==" 563 | "sWsAtQ==" 564 | "" 565 | "" 566 | ""; 567 | 568 | omemo_bundle * bundle_p = (void *) 0; 569 | assert_int_not_equal(omemo_bundle_import(bundle_malformed_1, &bundle_p), 0); 570 | assert_int_not_equal(omemo_bundle_import(bundle_malformed_2, &bundle_p), 0); 571 | assert_int_not_equal(omemo_bundle_import(bundle_malformed_3, &bundle_p), 0); 572 | assert_int_not_equal(omemo_bundle_import(bundle_malformed_4, &bundle_p), 0); 573 | assert_int_not_equal(omemo_bundle_import(bundle_malformed_5, &bundle_p), 0); 574 | 575 | } 576 | 577 | void test_bundle_import(void ** state) { 578 | (void) state; 579 | 580 | omemo_bundle * bundle_p = (void *) 0; 581 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 582 | assert_string_equal(bundle_p->device_id, "31415"); 583 | 584 | assert_ptr_not_equal(bundle_p->signed_pk_node_p, (void *) 0); 585 | assert_string_equal(mxmlGetElement(bundle_p->signed_pk_node_p), "signedPreKeyPublic"); 586 | 587 | assert_ptr_not_equal(bundle_p->signature_node_p, (void *) 0); 588 | assert_string_equal(mxmlGetElement(bundle_p->signature_node_p), "signedPreKeySignature"); 589 | 590 | assert_ptr_not_equal(bundle_p->identity_key_node_p, (void *) 0); 591 | assert_string_equal(mxmlGetElement(bundle_p->identity_key_node_p), "identityKey"); 592 | 593 | assert_ptr_not_equal(bundle_p->pre_keys_node_p, (void *) 0); 594 | assert_string_equal(mxmlGetElement(bundle_p->pre_keys_node_p), "prekeys"); 595 | 596 | assert_int_equal(bundle_p->pre_keys_amount, 4); 597 | 598 | omemo_bundle_destroy(bundle_p); 599 | } 600 | 601 | void test_bundle_get_device_id(void ** state) { 602 | (void) state; 603 | 604 | omemo_bundle * bundle_p = (void *) 0; 605 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 606 | assert_int_equal(omemo_bundle_get_device_id(bundle_p), 31415); 607 | 608 | omemo_bundle_destroy(bundle_p); 609 | } 610 | 611 | void test_bundle_get_signed_pre_key(void ** state) { 612 | (void) state; 613 | 614 | omemo_bundle * bundle_p = (void *) 0; 615 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 616 | 617 | uint32_t pre_key_id = 0; 618 | uint8_t * data_p = (void *) 0; 619 | size_t data_len; 620 | 621 | assert_int_equal(omemo_bundle_get_signed_pre_key(bundle_p, &pre_key_id, &data_p, &data_len), 0); 622 | assert_int_equal(pre_key_id, 1); 623 | assert_int_equal(data_len, 4); 624 | assert_memory_equal(&data[0], data_p, data_len); 625 | 626 | free(data_p); 627 | omemo_bundle_destroy(bundle_p); 628 | } 629 | 630 | void test_bundle_get_signature(void ** state) { 631 | (void) state; 632 | 633 | omemo_bundle * bundle_p = (void *) 0; 634 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 635 | 636 | uint8_t * data_p = (void *) 0; 637 | size_t data_len; 638 | 639 | assert_int_equal(omemo_bundle_get_signature(bundle_p, &data_p, &data_len), 0); 640 | assert_int_equal(data_len, 4); 641 | assert_memory_equal(&data[0], data_p, data_len); 642 | 643 | free(data_p); 644 | omemo_bundle_destroy(bundle_p); 645 | } 646 | 647 | void test_bundle_get_identity_key(void ** state) { 648 | (void) state; 649 | 650 | omemo_bundle * bundle_p = (void *) 0; 651 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 652 | 653 | uint8_t * data_p = (void *) 0; 654 | size_t data_len; 655 | 656 | assert_int_equal(omemo_bundle_get_identity_key(bundle_p, &data_p, &data_len), 0); 657 | assert_int_equal(data_len, 4); 658 | assert_memory_equal(&data[0], data_p, data_len); 659 | 660 | free(data_p); 661 | omemo_bundle_destroy(bundle_p); 662 | } 663 | 664 | void test_bundle_get_random_pre_key(void ** state) { 665 | (void) state; 666 | 667 | omemo_bundle * bundle_p = (void *) 0; 668 | assert_int_equal(omemo_bundle_import(bundle, &bundle_p), 0); 669 | 670 | uint32_t pre_key_id = 0; 671 | uint8_t * data_p = (void *) 0; 672 | size_t data_len; 673 | 674 | assert_int_equal(omemo_bundle_get_random_pre_key(bundle_p, &pre_key_id, &data_p, &data_len), 0); 675 | assert_int_equal(data_len, 4); 676 | assert_memory_equal(&data[0], data_p, data_len); 677 | 678 | free(data_p); 679 | omemo_bundle_destroy(bundle_p); 680 | } 681 | 682 | void test_message_prepare_encryption(void ** state) { 683 | (void) state; 684 | 685 | uint32_t sid = 1337; 686 | 687 | omemo_message * msg_p; 688 | assert_int_equal(omemo_message_prepare_encryption((void *) 0, sid, (void *) 0, OMEMO_STRIP_NONE, (void *) 0), OMEMO_ERR_NULL); 689 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, (void *) 0, OMEMO_STRIP_NONE, (void *) 0), OMEMO_ERR_NULL); 690 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, (void *) 0), OMEMO_ERR_NULL); 691 | assert_int_equal(omemo_message_prepare_encryption("asdf", sid, &crypto, OMEMO_STRIP_NONE, &msg_p), OMEMO_ERR_MALFORMED_XML); 692 | 693 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 694 | 695 | assert_ptr_not_equal(msg_p, (void *) 0); 696 | assert_ptr_not_equal(msg_p->message_node_p, (void *) 0); 697 | assert_ptr_not_equal(msg_p->header_node_p, (void *) 0); 698 | assert_ptr_not_equal(msg_p->payload_node_p, (void *) 0); 699 | assert_ptr_not_equal(msg_p->key_p, (void *) 0); 700 | assert_int_equal(msg_p->key_len, OMEMO_AES_128_KEY_LENGTH); 701 | 702 | assert_string_equal(mxmlGetElement(msg_p->message_node_p), "message"); 703 | assert_string_equal(mxmlElementGetAttr(msg_p->message_node_p, "xmlns"), "jabber:client"); 704 | assert_string_equal(mxmlElementGetAttr(msg_p->message_node_p, "type"), "chat"); 705 | assert_string_equal(mxmlElementGetAttr(msg_p->message_node_p, "to"), "bob@example.com"); 706 | 707 | assert_string_equal(mxmlGetElement(msg_p->header_node_p), "header"); 708 | assert_string_equal(mxmlElementGetAttr(msg_p->header_node_p, "sid"), "1337"); 709 | mxml_node_t * iv_node_p = mxmlGetFirstChild(msg_p->header_node_p); 710 | assert_ptr_not_equal(iv_node_p, (void *) 0); 711 | assert_string_not_equal(mxmlGetOpaque(iv_node_p), ""); 712 | 713 | assert_string_equal(mxmlGetElement(msg_p->payload_node_p), "payload"); 714 | assert_string_not_equal(mxmlGetOpaque(msg_p->payload_node_p), ""); 715 | 716 | omemo_message_destroy(msg_p); 717 | } 718 | 719 | void test_message_prepare_encryption_with_extra_data(void ** state) { 720 | (void) state; 721 | 722 | uint32_t sid = 1337; 723 | 724 | omemo_message * msg_p; 725 | assert_int_equal(omemo_message_prepare_encryption(msg_out_extra, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 726 | 727 | assert_ptr_not_equal(msg_p, (void *) 0); 728 | assert_ptr_not_equal(msg_p->message_node_p, (void *) 0); 729 | assert_ptr_not_equal(msg_p->header_node_p, (void *) 0); 730 | assert_ptr_not_equal(msg_p->payload_node_p, (void *) 0); 731 | assert_ptr_not_equal(msg_p->key_p, (void *) 0); 732 | assert_int_equal(msg_p->key_len, OMEMO_AES_128_KEY_LENGTH); 733 | 734 | mxml_node_t * active_node_p = mxmlGetFirstChild(msg_p->message_node_p); 735 | assert_ptr_not_equal(active_node_p, (void *) 0); 736 | assert_string_equal(mxmlGetElement(active_node_p), "active"); 737 | 738 | assert_string_equal(mxmlGetElement(msg_p->header_node_p), "header"); 739 | assert_string_equal(mxmlElementGetAttr(msg_p->header_node_p, "sid"), "1337"); 740 | mxml_node_t * iv_node_p = mxmlGetFirstChild(msg_p->header_node_p); 741 | assert_ptr_not_equal(iv_node_p, (void *) 0); 742 | assert_string_not_equal(mxmlGetOpaque(iv_node_p), ""); 743 | 744 | assert_string_equal(mxmlGetElement(msg_p->payload_node_p), "payload"); 745 | assert_string_not_equal(mxmlGetOpaque(msg_p->payload_node_p), ""); 746 | 747 | omemo_message_destroy(msg_p); 748 | } 749 | 750 | void test_message_get_key(void ** state) { 751 | (void) state; 752 | 753 | uint32_t sid = 1234; 754 | 755 | omemo_message * msg_p; 756 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 757 | assert_ptr_not_equal(omemo_message_get_key(msg_p), (void *) 0); 758 | 759 | assert_int_equal(omemo_message_get_key_len(msg_p), OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH); 760 | 761 | omemo_message_destroy(msg_p); 762 | } 763 | 764 | void test_message_get_encrypted_key(void ** state) { 765 | (void) state; 766 | 767 | char * msg = "" 768 | "" 769 | "
" 770 | "sWsAtQ==" 771 | "BASE64ENCODED" 772 | "
" 773 | "BASE64ENCODED" 774 | "
" 775 | "" 776 | "
"; 777 | 778 | omemo_message * msg_p; 779 | assert_int_equal(omemo_message_prepare_decryption(msg, &msg_p), 0); 780 | 781 | uint8_t * key_p; 782 | size_t key_len; 783 | assert_int_equal(omemo_message_get_encrypted_key(msg_p, 1111, &key_p, &key_len), 0); 784 | assert_ptr_equal(key_p, (void *) 0); 785 | 786 | assert_int_equal(omemo_message_get_encrypted_key(msg_p, 2222, &key_p, &key_len), 0); 787 | assert_int_equal(key_len, 4); 788 | assert_memory_equal(key_p, data, key_len); 789 | 790 | omemo_message_destroy(msg_p); 791 | } 792 | 793 | // Only if the "prekey" attribute is "true" or "1", the function returns that it is a prekey. 794 | void test_message_is_encrypted_key_prekey(void ** state) { 795 | (void) state; 796 | 797 | char * msg = "" 798 | "" 799 | "
" 800 | "sWsAtQ==" 801 | "sWsAtQ==" 802 | "sWsAtQ==" 803 | "sWsAtQ==" 804 | "sWsAtQ==" 805 | "sWsAtQ==" 806 | "sWsAtQ==" 807 | "sWsAtQ==" 808 | "BASE64ENCODED" 809 | "
" 810 | "BASE64ENCODED" 811 | "
" 812 | "" 813 | "
"; 814 | 815 | omemo_message * msg_p; 816 | assert_int_equal(omemo_message_prepare_decryption(msg, &msg_p), 0); 817 | 818 | bool is_prekey = false; 819 | 820 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 2222, &is_prekey), 0); 821 | assert_int_equal(is_prekey, true); 822 | 823 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 3333, &is_prekey), 0); 824 | assert_int_equal(is_prekey, true); 825 | 826 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 4444, &is_prekey), 0); 827 | assert_int_equal(is_prekey, true); 828 | 829 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 5555, &is_prekey), 0); 830 | assert_int_equal(is_prekey, false); 831 | 832 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 6666, &is_prekey), 0); 833 | assert_int_equal(is_prekey, false); 834 | 835 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 7777, &is_prekey), 0); 836 | assert_int_equal(is_prekey, false); 837 | 838 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 8888, &is_prekey), 0); 839 | assert_int_equal(is_prekey, false); 840 | 841 | assert_int_equal(omemo_message_is_encrypted_key_prekey(msg_p, 9999, &is_prekey), 0); 842 | assert_int_equal(is_prekey, false); 843 | } 844 | 845 | void test_message_get_encrypted_key_after_iv(void ** state) { 846 | (void) state; 847 | 848 | char * msg = "" 849 | "" 850 | "
" 851 | "BASE64ENCODED" 852 | "sWsAtQ==" 853 | "
" 854 | "BASE64ENCODED" 855 | "
" 856 | "" 857 | "
"; 858 | 859 | omemo_message * msg_p; 860 | assert_int_equal(omemo_message_prepare_decryption(msg, &msg_p), 0); 861 | 862 | uint8_t * key_p; 863 | size_t key_len; 864 | assert_int_equal(omemo_message_get_encrypted_key(msg_p, 1111, &key_p, &key_len), 0); 865 | assert_ptr_equal(key_p, (void *) 0); 866 | 867 | assert_int_equal(omemo_message_get_encrypted_key(msg_p, 2222, &key_p, &key_len), 0); 868 | assert_int_equal(key_len, 4); 869 | assert_memory_equal(key_p, data, key_len); 870 | 871 | omemo_message_destroy(msg_p); 872 | } 873 | 874 | void test_message_get_encrypted_key_no_keys(void ** state) { 875 | (void) state; 876 | 877 | char * msg = "" 878 | "" 879 | "
" 880 | "BASE64ENCODED" 881 | "
" 882 | "BASE64ENCODED" 883 | "
" 884 | "" 885 | "
"; 886 | 887 | omemo_message * msg_p; 888 | assert_int_equal(omemo_message_prepare_decryption(msg, &msg_p), 0); 889 | 890 | uint8_t * key_p; 891 | size_t key_len; 892 | assert_int_equal(omemo_message_get_encrypted_key(msg_p, 1111, &key_p, &key_len), 0); 893 | assert_ptr_equal(key_p, (void *) 0); 894 | 895 | omemo_message_destroy(msg_p); 896 | } 897 | 898 | void test_message_add_recipient(void ** state) { 899 | (void) state; 900 | 901 | uint32_t sid = 4321; 902 | uint32_t rid = 1234; 903 | 904 | omemo_message * msg_p; 905 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 906 | 907 | assert_int_equal(omemo_message_add_recipient((void *) 0, rid, (void *) 0, 0), OMEMO_ERR_NULL); 908 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, (void *) 0, 0), OMEMO_ERR_NULL); 909 | 910 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 911 | 912 | mxml_node_t * key_node_p = mxmlGetFirstChild(msg_p->header_node_p); 913 | assert_ptr_not_equal(key_node_p, (void *) 0); 914 | assert_string_equal(mxmlGetElement(key_node_p), "key"); 915 | assert_string_equal(mxmlElementGetAttr(key_node_p, "rid"), "1234"); 916 | assert_string_equal(mxmlGetOpaque(key_node_p), data_b64); 917 | 918 | assert_int_equal(omemo_message_add_recipient(msg_p, sid, &data[0], 4), 0); 919 | key_node_p = mxmlGetNextSibling(key_node_p); 920 | assert_ptr_not_equal(key_node_p, (void *) 0); 921 | 922 | mxml_node_t * iv_node_p = mxmlGetLastChild(msg_p->header_node_p); 923 | assert_ptr_not_equal(iv_node_p, (void *) 0); 924 | assert_string_equal(mxmlGetElement(iv_node_p), "iv"); 925 | 926 | omemo_message_destroy(msg_p); 927 | } 928 | 929 | // The prekey attribute is added to the key element. 930 | void test_message_add_recipient_w_prekey(void ** state) { 931 | (void) state; 932 | 933 | uint32_t sid = 4321; 934 | uint32_t rid = 1234; 935 | 936 | omemo_message * msg_p; 937 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 938 | 939 | assert_int_equal(omemo_message_add_recipient_w_prekey(msg_p, rid, &data[0], 4), 0); 940 | 941 | mxml_node_t * key_node_p = mxmlGetFirstChild(msg_p->header_node_p); 942 | assert_ptr_not_equal(key_node_p, (void *) 0); 943 | assert_string_equal(mxmlGetElement(key_node_p), "key"); 944 | assert_string_equal(mxmlElementGetAttr(key_node_p, "rid"), "1234"); 945 | assert_string_equal(mxmlElementGetAttr(key_node_p, "prekey"), "true"); 946 | assert_string_equal(mxmlGetOpaque(key_node_p), data_b64); 947 | 948 | mxml_node_t * iv_node_p = mxmlGetLastChild(msg_p->header_node_p); 949 | assert_ptr_not_equal(iv_node_p, (void *) 0); 950 | assert_string_equal(mxmlGetElement(iv_node_p), "iv"); 951 | 952 | omemo_message_destroy(msg_p); 953 | } 954 | 955 | void test_message_export_encrypted(void ** state) { 956 | (void) state; 957 | 958 | uint32_t sid = 4321; 959 | uint32_t rid = 1234; 960 | 961 | omemo_message * msg_p; 962 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 963 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 964 | 965 | char * xml; 966 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_NONE, &xml), 0); 967 | 968 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 969 | assert_ptr_not_equal(message_node_p, (void *) 0); 970 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 971 | 972 | mxml_node_t * encrypted_node_p; 973 | assert_int_equal(expect_next_node(message_node_p, mxmlGetFirstChild, "encrypted", &encrypted_node_p), 0); 974 | assert_string_equal(mxmlElementGetAttr(encrypted_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 975 | 976 | mxml_node_t * header_node_p; 977 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetFirstChild, "header", &header_node_p), 0); 978 | assert_string_equal(mxmlElementGetAttr(header_node_p, "sid"), "4321"); 979 | 980 | mxml_node_t * payload_node_p; 981 | assert_int_equal(expect_next_node(header_node_p, mxmlGetNextSibling, "payload", &payload_node_p), 0); 982 | 983 | mxml_node_t * store_node_p; 984 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetNextSibling, "store", &store_node_p), 0); 985 | assert_string_equal(mxmlElementGetAttr(store_node_p, "xmlns"), "urn:xmpp:hints"); 986 | 987 | mxmlDelete(message_node_p); 988 | free(xml); 989 | omemo_message_destroy(msg_p); 990 | } 991 | 992 | void test_message_export_encrypted_with_extra_tags_and_body(void ** state) { 993 | (void) state; 994 | 995 | uint32_t sid = 4321; 996 | uint32_t rid = 1234; 997 | 998 | omemo_message * msg_p; 999 | assert_int_equal(omemo_message_prepare_encryption(msg_out_extra, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1000 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 1001 | 1002 | char * xml; 1003 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_BODY, &xml), 0); 1004 | 1005 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 1006 | assert_ptr_not_equal(message_node_p, (void *) 0); 1007 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 1008 | 1009 | mxml_node_t * active_node_p; 1010 | assert_int_equal(expect_next_node(message_node_p, mxmlGetFirstChild, "active", &active_node_p), 0); 1011 | 1012 | mxml_node_t * body_node_p; 1013 | assert_int_equal(expect_next_node(active_node_p, mxmlGetNextSibling, "body", &body_node_p), 0); 1014 | 1015 | mxml_node_t * encrypted_node_p; 1016 | assert_int_equal(expect_next_node(body_node_p, mxmlGetNextSibling, "encrypted", &encrypted_node_p), 0); 1017 | assert_string_equal(mxmlElementGetAttr(encrypted_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 1018 | 1019 | mxml_node_t * header_node_p; 1020 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetFirstChild, "header", &header_node_p), 0); 1021 | assert_string_equal(mxmlElementGetAttr(header_node_p, "sid"), "4321"); 1022 | 1023 | mxml_node_t * payload_node_p; 1024 | assert_int_equal(expect_next_node(header_node_p, mxmlGetNextSibling, "payload", &payload_node_p), 0); 1025 | 1026 | mxml_node_t * store_node_p 1027 | ; 1028 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetNextSibling, "store", &store_node_p), 0); 1029 | assert_string_equal(mxmlElementGetAttr(store_node_p, "xmlns"), "urn:xmpp:hints"); 1030 | 1031 | mxmlDelete(message_node_p); 1032 | free(xml); 1033 | omemo_message_destroy(msg_p); 1034 | } 1035 | 1036 | void test_message_export_encrypted_strip_xhtml(void ** state) { 1037 | (void) state; 1038 | 1039 | char * msg_html = "" 1040 | "hello" 1041 | "" 1042 | "" 1043 | "

hello

" 1044 | "" 1045 | "" 1046 | "
"; 1047 | 1048 | uint32_t sid = 4321; 1049 | uint32_t rid = 1234; 1050 | 1051 | omemo_message * msg_p; 1052 | assert_int_equal(omemo_message_prepare_encryption(msg_html, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1053 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 1054 | 1055 | assert_int_equal(omemo_message_strip_possible_plaintext(msg_p), 0); 1056 | 1057 | char * xml; 1058 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_BODY, &xml), 0); 1059 | 1060 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 1061 | assert_ptr_not_equal(message_node_p, (void *) 0); 1062 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 1063 | 1064 | mxml_node_t * html_node_p = mxmlFindElement(message_node_p, message_node_p, "html", NULL, NULL, MXML_DESCEND_FIRST); 1065 | assert_ptr_equal(html_node_p, NULL); 1066 | 1067 | mxmlDelete(message_node_p); 1068 | free(xml); 1069 | omemo_message_destroy(msg_p); 1070 | } 1071 | 1072 | void test_message_export_encrypted_strip_multiple_body(void ** state) { 1073 | (void) state; 1074 | 1075 | char * msg_mult_bodies = "" 1076 | "hello" 1077 | "hallo" 1078 | "hola" 1079 | ""; 1080 | 1081 | uint32_t sid = 4321; 1082 | uint32_t rid = 1234; 1083 | 1084 | omemo_message * msg_p; 1085 | assert_int_equal(omemo_message_prepare_encryption(msg_mult_bodies, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1086 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 1087 | 1088 | assert_int_equal(omemo_message_strip_possible_plaintext(msg_p), 0); 1089 | 1090 | char * xml; 1091 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_NONE, &xml), 0); 1092 | 1093 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 1094 | assert_ptr_not_equal(message_node_p, (void *) 0); 1095 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 1096 | 1097 | mxml_node_t * body_node_p = mxmlFindElement(message_node_p, message_node_p, "body", NULL, NULL, MXML_DESCEND_FIRST); 1098 | assert_ptr_equal(body_node_p, NULL); 1099 | 1100 | mxmlDelete(message_node_p); 1101 | free(xml); 1102 | omemo_message_destroy(msg_p); 1103 | } 1104 | 1105 | void test_message_export_encrypted_strip_xhtml_and_body(void ** state) { 1106 | (void) state; 1107 | 1108 | char * msg_double_everything = "" 1109 | "hello" 1110 | "hallo" 1111 | "" 1112 | "" 1113 | "

hello

" 1114 | "" 1115 | "" 1116 | "

hallo

" 1117 | "" 1118 | "" 1119 | "
"; 1120 | 1121 | uint32_t sid = 4321; 1122 | uint32_t rid = 1234; 1123 | 1124 | omemo_message * msg_p; 1125 | assert_int_equal(omemo_message_prepare_encryption(msg_double_everything, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1126 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 1127 | 1128 | assert_int_equal(omemo_message_strip_possible_plaintext(msg_p), 0); 1129 | 1130 | char * xml; 1131 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_NONE, &xml), 0); 1132 | 1133 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 1134 | assert_ptr_not_equal(message_node_p, (void *) 0); 1135 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 1136 | 1137 | mxml_node_t * html_node_p = mxmlFindElement(message_node_p, message_node_p, "html", NULL, NULL, MXML_DESCEND_FIRST); 1138 | assert_ptr_equal(html_node_p, NULL); 1139 | 1140 | mxml_node_t * body_node_p = mxmlFindElement(message_node_p, message_node_p, "body", NULL, NULL, MXML_DESCEND_FIRST); 1141 | assert_ptr_equal(body_node_p, NULL); 1142 | 1143 | mxmlDelete(message_node_p); 1144 | free(xml); 1145 | omemo_message_destroy(msg_p); 1146 | } 1147 | 1148 | void test_message_export_encrypted_with_eme(void ** state) { 1149 | (void) state; 1150 | 1151 | uint32_t sid = 4321; 1152 | uint32_t rid = 1234; 1153 | 1154 | omemo_message * msg_p; 1155 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1156 | assert_int_equal(omemo_message_add_recipient(msg_p, rid, &data[0], 4), 0); 1157 | 1158 | char * xml; 1159 | assert_int_equal(omemo_message_export_encrypted(msg_p, OMEMO_ADD_MSG_EME, &xml), 0); 1160 | 1161 | mxml_node_t * message_node_p = mxmlLoadString((void *) 0, xml, MXML_OPAQUE_CALLBACK); 1162 | assert_ptr_not_equal(message_node_p, (void *) 0); 1163 | assert_string_equal(mxmlGetElement(message_node_p), "message"); 1164 | 1165 | mxml_node_t * encrypted_node_p; 1166 | assert_int_equal(expect_next_node(message_node_p, mxmlGetFirstChild, "encrypted", &encrypted_node_p), 0); 1167 | assert_string_equal(mxmlElementGetAttr(encrypted_node_p, "xmlns"), "eu.siacs.conversations.axolotl"); 1168 | 1169 | mxml_node_t * header_node_p; 1170 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetFirstChild, "header", &header_node_p), 0); 1171 | assert_string_equal(mxmlElementGetAttr(header_node_p, "sid"), "4321"); 1172 | 1173 | mxml_node_t * payload_node_p; 1174 | assert_int_equal(expect_next_node(header_node_p, mxmlGetNextSibling, "payload", &payload_node_p), 0); 1175 | 1176 | mxml_node_t * eme_node_p; 1177 | assert_int_equal(expect_next_node(encrypted_node_p, mxmlGetNextSibling, "encryption", &eme_node_p), 0); 1178 | assert_string_equal(mxmlElementGetAttr(eme_node_p, "xmlns"), "urn:xmpp:eme:0"); 1179 | assert_string_equal(mxmlElementGetAttr(eme_node_p, "namespace"), OMEMO_NS); 1180 | assert_string_equal(mxmlElementGetAttr(eme_node_p, "name"), "OMEMO"); 1181 | 1182 | mxml_node_t * store_node_p; 1183 | assert_int_equal(expect_next_node(eme_node_p, mxmlGetNextSibling, "store", &store_node_p), 0); 1184 | assert_string_equal(mxmlElementGetAttr(store_node_p, "xmlns"), "urn:xmpp:hints"); 1185 | 1186 | mxmlDelete(message_node_p); 1187 | free(xml); 1188 | omemo_message_destroy(msg_p); 1189 | } 1190 | 1191 | void test_message_encrypt_decrypt(void ** state) { 1192 | (void) state; 1193 | 1194 | uint32_t sid = 4321; 1195 | uint32_t rid = 1234; 1196 | 1197 | omemo_message * msg_out_p; 1198 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_out_p), 0); 1199 | 1200 | const uint8_t * key_p = omemo_message_get_key(msg_out_p); 1201 | 1202 | assert_int_equal(omemo_message_add_recipient(msg_out_p, rid, key_p, omemo_message_get_key_len(msg_out_p)), 0); 1203 | 1204 | char * xml_out; 1205 | assert_int_equal(omemo_message_export_encrypted(msg_out_p, OMEMO_ADD_MSG_NONE, &xml_out), 0); 1206 | 1207 | omemo_message * msg_in_p; 1208 | assert_int_equal(omemo_message_prepare_decryption(xml_out, &msg_in_p), 0); 1209 | 1210 | assert_ptr_not_equal(msg_in_p->header_node_p, (void *) 0); 1211 | assert_string_equal(mxmlGetElement(msg_in_p->message_node_p), "message"); 1212 | 1213 | assert_ptr_not_equal(msg_in_p->header_node_p, (void *) 0); 1214 | assert_string_equal(mxmlGetElement(msg_in_p->header_node_p), "header"); 1215 | 1216 | assert_ptr_not_equal(msg_in_p->payload_node_p, (void *) 0); 1217 | assert_string_equal(mxmlGetElement(msg_in_p->payload_node_p), "payload"); 1218 | 1219 | assert_ptr_equal(msg_in_p->key_p, (void *) 0); 1220 | 1221 | assert_int_equal(omemo_message_get_sender_id(msg_in_p), sid); 1222 | 1223 | uint8_t * key_retrieved_p; 1224 | size_t key_retrieved_len; 1225 | assert_int_equal(omemo_message_get_encrypted_key(msg_in_p, rid, &key_retrieved_p, &key_retrieved_len), 0); 1226 | assert_int_equal(key_retrieved_len, OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH); 1227 | assert_memory_equal(key_p, key_retrieved_p, key_retrieved_len); 1228 | 1229 | char * xml_in; 1230 | assert_int_equal(omemo_message_export_decrypted(msg_in_p, key_retrieved_p, key_retrieved_len, &crypto, &xml_in), 0); 1231 | mxml_node_t * message_node_decrypted_p = mxmlLoadString((void *) 0, xml_in, MXML_OPAQUE_CALLBACK); 1232 | assert_ptr_not_equal(message_node_decrypted_p, (void *) 0); 1233 | mxml_node_t * body_text_node_p = mxmlFindPath(message_node_decrypted_p, "body"); 1234 | assert_string_equal(mxmlGetOpaque(body_text_node_p), "hello"); 1235 | 1236 | omemo_message_destroy(msg_out_p); 1237 | omemo_message_destroy(msg_in_p); 1238 | free(xml_out); 1239 | free(xml_in); 1240 | free(key_retrieved_p); 1241 | } 1242 | 1243 | void test_message_encrypt_decrypt_with_extra_nodes(void ** state) { 1244 | (void) state; 1245 | 1246 | uint32_t sid = 4321; 1247 | uint32_t rid = 1234; 1248 | 1249 | omemo_message * msg_out_p; 1250 | assert_int_equal(omemo_message_prepare_encryption(msg_out_extra, sid, &crypto, OMEMO_STRIP_NONE, &msg_out_p), 0); 1251 | 1252 | const uint8_t * key_p = omemo_message_get_key(msg_out_p); 1253 | 1254 | assert_int_equal(omemo_message_add_recipient(msg_out_p, rid, key_p, omemo_message_get_key_len(msg_out_p)), 0); 1255 | 1256 | char * xml_out; 1257 | assert_int_equal(omemo_message_export_encrypted(msg_out_p, OMEMO_ADD_MSG_NONE, &xml_out), 0); 1258 | 1259 | omemo_message * msg_in_p; 1260 | assert_int_equal(omemo_message_prepare_decryption(xml_out, &msg_in_p), 0); 1261 | 1262 | assert_ptr_not_equal(msg_in_p->message_node_p, (void *) 0); 1263 | assert_string_equal(mxmlGetElement(msg_in_p->message_node_p), "message"); 1264 | 1265 | mxml_node_t * active_node_p; 1266 | assert_int_equal(expect_next_node(msg_in_p->message_node_p, mxmlGetFirstChild, "active", &active_node_p), 0); 1267 | 1268 | assert_ptr_not_equal(msg_in_p->header_node_p, (void *) 0); 1269 | assert_string_equal(mxmlGetElement(msg_in_p->header_node_p), "header"); 1270 | 1271 | assert_ptr_not_equal(msg_in_p->payload_node_p, (void *) 0); 1272 | assert_string_equal(mxmlGetElement(msg_in_p->payload_node_p), "payload"); 1273 | 1274 | assert_ptr_equal(msg_in_p->key_p, (void *) 0); 1275 | 1276 | assert_int_equal(omemo_message_get_sender_id(msg_in_p), sid); 1277 | 1278 | uint8_t * key_retrieved_p; 1279 | size_t key_retrieved_len; 1280 | assert_int_equal(omemo_message_get_encrypted_key(msg_in_p, rid, &key_retrieved_p, &key_retrieved_len), 0); 1281 | assert_int_equal(key_retrieved_len, OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH); 1282 | assert_memory_equal(key_p, key_retrieved_p, key_retrieved_len); 1283 | 1284 | char * xml_in; 1285 | assert_int_equal(omemo_message_export_decrypted(msg_in_p, key_retrieved_p, key_retrieved_len, &crypto, &xml_in), 0); 1286 | mxml_node_t * message_node_decrypted_p = mxmlLoadString((void *) 0, xml_in, MXML_OPAQUE_CALLBACK); 1287 | assert_ptr_not_equal(message_node_decrypted_p, (void *) 0); 1288 | mxml_node_t * body_text_node_p = mxmlFindPath(message_node_decrypted_p, "body"); 1289 | assert_string_equal(mxmlGetOpaque(body_text_node_p), "hello"); 1290 | 1291 | omemo_message_destroy(msg_out_p); 1292 | omemo_message_destroy(msg_in_p); 1293 | free(xml_out); 1294 | free(xml_in); 1295 | free(key_retrieved_p); 1296 | } 1297 | 1298 | void test_message_encrypt_decrypt_with_added_body(void ** state) { 1299 | (void) state; 1300 | 1301 | uint32_t sid = 4321; 1302 | uint32_t rid = 1234; 1303 | 1304 | omemo_message * msg_out_p; 1305 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_out_p), 0); 1306 | 1307 | const uint8_t * key_p = omemo_message_get_key(msg_out_p); 1308 | 1309 | assert_int_equal(omemo_message_add_recipient(msg_out_p, rid, key_p, omemo_message_get_key_len(msg_out_p)), 0); 1310 | 1311 | char * xml_out; 1312 | assert_int_equal(omemo_message_export_encrypted(msg_out_p, OMEMO_ADD_MSG_BODY, &xml_out), 0); 1313 | 1314 | omemo_message * msg_in_p; 1315 | assert_int_equal(omemo_message_prepare_decryption(xml_out, &msg_in_p), 0); 1316 | 1317 | assert_int_equal(omemo_message_get_sender_id(msg_in_p), sid); 1318 | 1319 | uint8_t * key_retrieved_p; 1320 | size_t key_retrieved_len; 1321 | assert_int_equal(omemo_message_get_encrypted_key(msg_in_p, rid, &key_retrieved_p, &key_retrieved_len), 0); 1322 | assert_int_equal(key_retrieved_len, OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH); 1323 | assert_memory_equal(key_p, key_retrieved_p, key_retrieved_len); 1324 | 1325 | char * xml_in; 1326 | assert_int_equal(omemo_message_export_decrypted(msg_in_p, key_retrieved_p, key_retrieved_len, &crypto, &xml_in), 0); 1327 | mxml_node_t * message_node_decrypted_p = mxmlLoadString((void *) 0, xml_in, MXML_OPAQUE_CALLBACK); 1328 | assert_ptr_not_equal(message_node_decrypted_p, (void *) 0); 1329 | mxml_node_t * body_text_node_p = mxmlFindPath(message_node_decrypted_p, "body"); 1330 | assert_string_equal(mxmlGetOpaque(body_text_node_p), "hello"); 1331 | 1332 | omemo_message_destroy(msg_out_p); 1333 | omemo_message_destroy(msg_in_p); 1334 | free(xml_out); 1335 | free(xml_in); 1336 | free(key_retrieved_p); 1337 | } 1338 | 1339 | void test_message_encrypt_decrypt_with_added_eme(void ** state) { 1340 | (void) state; 1341 | 1342 | uint32_t sid = 4321; 1343 | uint32_t rid = 1234; 1344 | 1345 | omemo_message * msg_out_p; 1346 | assert_int_equal(omemo_message_prepare_encryption(msg_out, sid, &crypto, OMEMO_STRIP_NONE, &msg_out_p), 0); 1347 | 1348 | const uint8_t * key_p = omemo_message_get_key(msg_out_p); 1349 | 1350 | assert_int_equal(omemo_message_add_recipient(msg_out_p, rid, key_p, omemo_message_get_key_len(msg_out_p)), 0); 1351 | 1352 | char * xml_out; 1353 | assert_int_equal(omemo_message_export_encrypted(msg_out_p, OMEMO_ADD_MSG_EME, &xml_out), 0); 1354 | 1355 | omemo_message * msg_in_p; 1356 | assert_int_equal(omemo_message_prepare_decryption(xml_out, &msg_in_p), 0); 1357 | 1358 | assert_int_equal(omemo_message_get_sender_id(msg_in_p), sid); 1359 | 1360 | uint8_t * key_retrieved_p; 1361 | size_t key_retrieved_len; 1362 | assert_int_equal(omemo_message_get_encrypted_key(msg_in_p, rid, &key_retrieved_p, &key_retrieved_len), 0); 1363 | assert_int_equal(key_retrieved_len, OMEMO_AES_128_KEY_LENGTH + OMEMO_AES_GCM_TAG_LENGTH); 1364 | assert_memory_equal(key_p, key_retrieved_p, key_retrieved_len); 1365 | 1366 | char * xml_in; 1367 | assert_int_equal(omemo_message_export_decrypted(msg_in_p, key_retrieved_p, key_retrieved_len, &crypto, &xml_in), 0); 1368 | mxml_node_t * message_node_decrypted_p = mxmlLoadString((void *) 0, xml_in, MXML_OPAQUE_CALLBACK); 1369 | assert_ptr_not_equal(message_node_decrypted_p, (void *) 0); 1370 | mxml_node_t * body_text_node_p = mxmlFindPath(message_node_decrypted_p, "body"); 1371 | assert_string_equal(mxmlGetOpaque(body_text_node_p), "hello"); 1372 | 1373 | mxml_node_t * eme_node_p = mxmlFindPath(message_node_decrypted_p, "encryption"); 1374 | assert_ptr_equal(eme_node_p, (void *) 0); 1375 | 1376 | omemo_message_destroy(msg_out_p); 1377 | omemo_message_destroy(msg_in_p); 1378 | free(xml_out); 1379 | free(xml_in); 1380 | free(key_retrieved_p); 1381 | } 1382 | 1383 | void test_message_get_names(void ** state) { 1384 | (void) state; 1385 | 1386 | char * msg = "" 1387 | "hello" 1388 | ""; 1389 | 1390 | omemo_message * msg_p; 1391 | assert_int_equal(omemo_message_prepare_encryption(msg, 1337, &crypto, OMEMO_STRIP_NONE, &msg_p), 0); 1392 | assert_string_equal(omemo_message_get_sender_name_full(msg_p), "alice@example.com/hurr"); 1393 | assert_string_equal(omemo_message_get_sender_name_bare(msg_p), "alice@example.com"); 1394 | assert_string_equal(omemo_message_get_recipient_name_full(msg_p), "bob@example.com"); 1395 | assert_string_equal(omemo_message_get_recipient_name_bare(msg_p), "bob@example.com"); 1396 | 1397 | omemo_message_destroy(msg_p); 1398 | } 1399 | 1400 | int main(void) { 1401 | const struct CMUnitTest tests[] = { 1402 | cmocka_unit_test(test_devicelist_create), 1403 | cmocka_unit_test(test_devicelist_import), 1404 | cmocka_unit_test(test_devicelist_import_empty), 1405 | cmocka_unit_test(test_devicelist_import_empty_alt), 1406 | cmocka_unit_test(test_devicelist_add), 1407 | cmocka_unit_test(test_devicelist_contains_id), 1408 | cmocka_unit_test(test_devicelist_remove), 1409 | cmocka_unit_test(test_devicelist_export), 1410 | cmocka_unit_test(test_devicelist_get_pep_node_name), 1411 | cmocka_unit_test(test_devicelist_get_id_list), 1412 | cmocka_unit_test(test_devicelist_diff), 1413 | 1414 | cmocka_unit_test(test_bundle_create), 1415 | cmocka_unit_test(test_bundle_set_device_id), 1416 | cmocka_unit_test(test_bundle_set_signed_pre_key), 1417 | cmocka_unit_test(test_bundle_set_signature), 1418 | cmocka_unit_test(test_bundle_set_identity_key), 1419 | cmocka_unit_test(test_bundle_add_pre_key), 1420 | cmocka_unit_test(test_bundle_export), 1421 | cmocka_unit_test(test_bundle_get_pep_node_name), 1422 | cmocka_unit_test(test_bundle_import_malformed), 1423 | cmocka_unit_test(test_bundle_import), 1424 | cmocka_unit_test(test_bundle_get_device_id), 1425 | cmocka_unit_test(test_bundle_get_signed_pre_key), 1426 | cmocka_unit_test(test_bundle_get_signature), 1427 | cmocka_unit_test(test_bundle_get_identity_key), 1428 | cmocka_unit_test(test_bundle_get_random_pre_key), 1429 | 1430 | cmocka_unit_test(test_message_prepare_encryption), 1431 | cmocka_unit_test(test_message_prepare_encryption_with_extra_data), 1432 | cmocka_unit_test(test_message_get_key), 1433 | cmocka_unit_test(test_message_get_encrypted_key), 1434 | cmocka_unit_test(test_message_get_encrypted_key_after_iv), 1435 | cmocka_unit_test(test_message_get_encrypted_key_no_keys), 1436 | cmocka_unit_test(test_message_is_encrypted_key_prekey), 1437 | cmocka_unit_test(test_message_add_recipient), 1438 | cmocka_unit_test(test_message_add_recipient_w_prekey), 1439 | cmocka_unit_test(test_message_export_encrypted), 1440 | cmocka_unit_test(test_message_export_encrypted_strip_xhtml), 1441 | cmocka_unit_test(test_message_export_encrypted_strip_multiple_body), 1442 | cmocka_unit_test(test_message_export_encrypted_strip_xhtml_and_body), 1443 | cmocka_unit_test(test_message_export_encrypted_with_extra_tags_and_body), 1444 | cmocka_unit_test(test_message_export_encrypted_with_eme), 1445 | cmocka_unit_test(test_message_encrypt_decrypt), 1446 | cmocka_unit_test(test_message_encrypt_decrypt_with_extra_nodes), 1447 | cmocka_unit_test(test_message_encrypt_decrypt_with_added_body), 1448 | cmocka_unit_test(test_message_encrypt_decrypt_with_added_eme), 1449 | cmocka_unit_test(test_message_get_names) 1450 | }; 1451 | 1452 | return cmocka_run_group_tests(tests, NULL, NULL); 1453 | } 1454 | -------------------------------------------------------------------------------- /test/test_storage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "../src/libomemo.h" 9 | #include "../src/libomemo.c" 10 | #include "../src/libomemo_storage.c" 11 | 12 | #define TEST_DB_PATH "test.sqlite" 13 | 14 | int db_cleanup(void ** state) { 15 | (void) state; 16 | 17 | remove(TEST_DB_PATH); 18 | 19 | return 0; 20 | } 21 | 22 | void test_test(void ** state) { 23 | (void) state; 24 | 25 | int a = 1; 26 | 27 | printf("%s\n", STMT_PROLOG); 28 | printf("a: %i, -a: %i\n", a, -a); 29 | } 30 | 31 | void test_devicelist_save_id(void ** state) { 32 | (void) state; 33 | 34 | int ret_val = omemo_storage_user_device_id_save("alice", 1337, TEST_DB_PATH); 35 | assert_int_equal(ret_val, 0); 36 | 37 | sqlite3 * db_p; 38 | assert_int_equal(sqlite3_open(TEST_DB_PATH, &db_p), SQLITE_OK); 39 | 40 | char * stmt = "SELECT * FROM devicelists WHERE name IS 'alice';"; 41 | sqlite3_stmt * pstmt_p; 42 | assert_int_equal(sqlite3_prepare_v2(db_p, stmt, -1, &pstmt_p, (void *) 0), SQLITE_OK); 43 | 44 | assert_int_equal(sqlite3_step(pstmt_p), SQLITE_ROW); 45 | assert_int_equal(sqlite3_column_int(pstmt_p, 1), 1337); 46 | assert_int_equal(sqlite3_step(pstmt_p), SQLITE_DONE); 47 | 48 | sqlite3_finalize(pstmt_p); 49 | sqlite3_close(db_p); 50 | } 51 | 52 | void test_devicelist_delete_id(void ** state){ 53 | (void) state; 54 | 55 | assert_int_equal(omemo_storage_user_device_id_save("alice", 1111, TEST_DB_PATH), 0); 56 | assert_int_equal(omemo_storage_global_device_id_exists(1111, TEST_DB_PATH), 1); 57 | assert_int_equal(omemo_storage_user_device_id_delete("alice", 2222, TEST_DB_PATH), 0); 58 | assert_int_equal(omemo_storage_global_device_id_exists(1111, TEST_DB_PATH), 1); 59 | assert_int_equal(omemo_storage_user_device_id_delete("alice", 1111, TEST_DB_PATH), 0); 60 | assert_int_equal(omemo_storage_global_device_id_exists(1111, TEST_DB_PATH), 0); 61 | } 62 | 63 | void test_devicelist_retrieve(void ** state) { 64 | (void) state; 65 | 66 | omemo_devicelist * dl_p; 67 | 68 | assert_int_equal(omemo_storage_user_devicelist_retrieve("alice", TEST_DB_PATH, &dl_p), 0); 69 | assert_ptr_not_equal(dl_p, (void *) 0); 70 | assert_ptr_equal(omemo_devicelist_get_id_list(dl_p), (void *) 0); 71 | omemo_devicelist_destroy(dl_p); 72 | 73 | assert_int_equal(omemo_storage_user_device_id_save("alice", 1337, TEST_DB_PATH), 0); 74 | assert_int_equal(omemo_storage_user_devicelist_retrieve("alice", TEST_DB_PATH, &dl_p), 0); 75 | 76 | assert_ptr_not_equal(dl_p, (void *) 0); 77 | assert_int_equal(omemo_devicelist_list_data(omemo_devicelist_get_id_list(dl_p)), 1337); 78 | assert_ptr_equal((omemo_devicelist_get_id_list(dl_p))->next, (void *) 0); 79 | omemo_devicelist_destroy(dl_p); 80 | 81 | assert_int_equal(omemo_storage_user_device_id_save("alice", 1338, TEST_DB_PATH), 0); 82 | assert_int_equal(omemo_storage_user_devicelist_retrieve("alice", TEST_DB_PATH, &dl_p), 0); 83 | assert_ptr_not_equal(dl_p, (void *) 0); 84 | assert_int_equal(omemo_devicelist_list_data(omemo_devicelist_get_id_list(dl_p)), 1337); 85 | assert_int_equal(omemo_devicelist_list_data(omemo_devicelist_get_id_list(dl_p)), 1337); 86 | assert_int_equal(omemo_devicelist_list_data(omemo_devicelist_get_id_list(dl_p)->next), 1338); 87 | 88 | omemo_devicelist_destroy(dl_p); 89 | } 90 | 91 | void test_chatlist_save(void ** state) { 92 | (void) state; 93 | 94 | int ret_val = omemo_storage_chatlist_save("test", TEST_DB_PATH); 95 | assert_int_equal(ret_val, 0); 96 | 97 | sqlite3 * db_p; 98 | assert_int_equal(sqlite3_open(TEST_DB_PATH, &db_p), SQLITE_OK); 99 | 100 | char * stmt = "SELECT * FROM cl WHERE chat_name IS 'test';"; 101 | sqlite3_stmt * pstmt_p; 102 | assert_int_equal(sqlite3_prepare_v2(db_p, stmt, -1, &pstmt_p, (void *) 0), SQLITE_OK); 103 | 104 | assert_int_equal(sqlite3_step(pstmt_p), SQLITE_ROW); 105 | assert_string_equal(sqlite3_column_text(pstmt_p, 0), "test"); 106 | assert_int_equal(sqlite3_step(pstmt_p), SQLITE_DONE); 107 | 108 | sqlite3_finalize(pstmt_p); 109 | sqlite3_close(db_p); 110 | } 111 | 112 | void test_chatlist_exists(void ** state) { 113 | (void) state; 114 | 115 | assert_int_equal(omemo_storage_chatlist_exists("test", TEST_DB_PATH), 0); 116 | assert_int_equal(omemo_storage_chatlist_save("test", TEST_DB_PATH), 0); 117 | assert_int_equal(omemo_storage_chatlist_exists("test", TEST_DB_PATH), 1); 118 | } 119 | 120 | void test_chatlist_delete(void ** state) { 121 | (void) state; 122 | 123 | assert_int_equal(omemo_storage_chatlist_exists("test", TEST_DB_PATH), 0); 124 | assert_int_equal(omemo_storage_chatlist_save("test", TEST_DB_PATH), 0); 125 | assert_int_equal(omemo_storage_chatlist_exists("test", TEST_DB_PATH), 1); 126 | assert_int_equal(omemo_storage_chatlist_delete("test", TEST_DB_PATH), 0); 127 | assert_int_equal(omemo_storage_chatlist_exists("test", TEST_DB_PATH), 0); 128 | } 129 | 130 | void test_global_device_id_exists(void ** state) { 131 | (void) state; 132 | 133 | assert_int_equal(omemo_storage_global_device_id_exists(55555, TEST_DB_PATH), 0); 134 | assert_int_equal(omemo_storage_user_device_id_save("alice", 11111, TEST_DB_PATH), 0); 135 | assert_int_equal(omemo_storage_global_device_id_exists(55555, TEST_DB_PATH), 0); 136 | assert_int_equal(omemo_storage_user_device_id_save("alice", 11112, TEST_DB_PATH), 0); 137 | assert_int_equal(omemo_storage_global_device_id_exists(55555, TEST_DB_PATH), 0); 138 | assert_int_equal(omemo_storage_user_device_id_save("bob", 21111, TEST_DB_PATH), 0); 139 | assert_int_equal(omemo_storage_global_device_id_exists(55555, TEST_DB_PATH), 0); 140 | assert_int_equal(omemo_storage_user_device_id_save("bob", 55555, TEST_DB_PATH), 0); 141 | assert_int_equal(omemo_storage_global_device_id_exists(55555, TEST_DB_PATH), 1); 142 | } 143 | 144 | int main(void) { 145 | const struct CMUnitTest tests[] = { 146 | //cmocka_unit_test(test_test), 147 | cmocka_unit_test_teardown(test_devicelist_save_id, db_cleanup), 148 | cmocka_unit_test_teardown(test_devicelist_delete_id, db_cleanup), 149 | cmocka_unit_test_teardown(test_devicelist_retrieve, db_cleanup), 150 | cmocka_unit_test_teardown(test_chatlist_save, db_cleanup), 151 | cmocka_unit_test_teardown(test_chatlist_exists, db_cleanup), 152 | cmocka_unit_test_teardown(test_chatlist_delete, db_cleanup), 153 | cmocka_unit_test_teardown(test_global_device_id_exists, db_cleanup) 154 | }; 155 | 156 | return cmocka_run_group_tests(tests, (void *) 0, (void *) 0); 157 | } 158 | --------------------------------------------------------------------------------