├── .gitignore ├── Makefile ├── c_src ├── Makefile ├── base64.cpp ├── base64.h ├── erl_signal_client.cpp ├── erl_signal_client.h ├── erl_signal_client_storage.cpp ├── erl_signal_client_storage.h ├── erl_signal_crypto.cpp ├── erl_signal_crypto.h ├── erl_signal_log.cpp ├── erl_signal_log.h ├── erl_signal_nif.cpp ├── erl_signal_store.cpp ├── erl_signal_store.h └── libsignal-c ├── erl_signal ├── erl_signal.cbp ├── erl_signal.layout ├── include └── erl_signal.hrl ├── libs ├── .gitignore ├── Makefile └── axc │ ├── axc.c │ ├── axc.h │ ├── axc_crypto.c │ ├── axc_crypto.h │ ├── axc_crypto.o │ ├── axc_store.c │ └── axc_store.h ├── license.txt ├── readme.md ├── rebar.config ├── rebar.lock ├── scripts └── compile.sh ├── src ├── erl_signal.app.src ├── erl_signal.erl └── erl_signal_nif.erl └── test ├── cpp └── Makefile └── main_SUITE.erl /.gitignore: -------------------------------------------------------------------------------- 1 | /rebar3 2 | .rebar 3 | .idea 4 | .vscode 5 | _build 6 | test/ct_logs 7 | *.iml 8 | *.dump 9 | c_src/*.o 10 | priv/*.so -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: compile 2 | 3 | compile: 4 | ./rebar3 compile 5 | 6 | run: 7 | ./rebar3 shell 8 | 9 | .PHONY: test 10 | test: 11 | rm -rf test/ct_logs 12 | ./rebar3 ct 13 | 14 | .PHONY: clean 15 | clean: 16 | rm -rf test/ct_logs 17 | ./rebar3 clean 18 | 19 | -------------------------------------------------------------------------------- /c_src/Makefile: -------------------------------------------------------------------------------- 1 | # Based on c_src.mk from erlang.mk by Loic Hoguin 2 | 3 | PROJECT := erl_signal_nif 4 | 5 | CURDIR := $(shell pwd) 6 | BASEDIR := $(abspath $(CURDIR)/..) 7 | 8 | PROJECT ?= $(notdir $(BASEDIR)) 9 | PROJECT := $(strip $(PROJECT)) 10 | 11 | ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).") 12 | ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).") 13 | ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).") 14 | 15 | C_SRC_DIR = $(CURDIR) $(CURDIR)/libsignal-c 16 | C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so 17 | 18 | # System type and C compiler/flags. 19 | 20 | UNAME_SYS := $(shell uname -s) 21 | ifeq ($(UNAME_SYS), Darwin) 22 | CC ?= cc 23 | CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes 24 | CXXFLAGS ?= -O3 -std=c++11 -arch x86_64 -finline-functions -Wall 25 | LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress 26 | else ifeq ($(UNAME_SYS), FreeBSD) 27 | CC ?= cc 28 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 29 | CXXFLAGS ?= -O3 -std=c++11 -finline-functions -Wall 30 | else ifeq ($(UNAME_SYS), Linux) 31 | CC ?= gcc 32 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 33 | CXXFLAGS ?= -O3 -std=c++11 -finline-functions -Wall 34 | endif 35 | 36 | CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/c++/6/ -I /usr/include/x86_64-linux-gnu/c++/6/ 37 | CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/c++/6/ -I /usr/include/x86_64-linux-gnu/c++/6/ 38 | 39 | LDLIBS += -L ../libs/cJSON 40 | LDLIBS += -L ../libs/libsignal-protocol-c/build/src -lsignal-protocol-c 41 | LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei 42 | LDLIBS += -L /usr/lib32 -lgcrypt 43 | # LDLIBS += -L /usr/lib32 -lglib 44 | 45 | 46 | LDFLAGS += -shared 47 | 48 | # Verbosity. 49 | 50 | c_verbose_0 = @echo " C " $(?F); 51 | c_verbose = $(c_verbose_$(V)) 52 | 53 | cpp_verbose_0 = @echo " CPP " $(?F); 54 | cpp_verbose = $(cpp_verbose_$(V)) 55 | 56 | link_verbose_0 = @echo " LD " $(@F); 57 | link_verbose = $(link_verbose_$(V)) 58 | 59 | SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) 60 | OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) 61 | # OBJECTS += $(shell find ../libs/libsignal-protocol-c/build/src -type f \( -name "*.o" \)) 62 | 63 | 64 | COMPILE_C = CPATH=CPATH:libsignal-c $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c 65 | COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c 66 | 67 | $(C_SRC_OUTPUT): Makefile $(OBJECTS) 68 | @mkdir -p $(BASEDIR)/priv/ 69 | $(link_verbose) g++ $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) 70 | 71 | %.o: %.c 72 | $(COMPILE_C) $(OUTPUT_OPTION) $< 73 | 74 | %.o: %.cc 75 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 76 | 77 | %.o: %.C 78 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 79 | 80 | %.o: %.cpp 81 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 82 | 83 | clean: 84 | @rm -f $(C_SRC_OUTPUT) $(OBJECTS) 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /c_src/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | base64 encoding and decoding with C++. 5 | 6 | Version: 1.01.00 7 | 8 | Copyright (C) 2004-2017 René Nyffenegger 9 | 10 | This source code is provided 'as-is', without any express or implied 11 | warranty. In no event will the author be held liable for any damages 12 | arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any purpose, 15 | including commercial applications, and to alter it and redistribute it 16 | freely, subject to the following restrictions: 17 | 18 | 1. The origin of this source code must not be misrepresented; you must not 19 | claim that you wrote the original source code. If you use this source code 20 | in a product, an acknowledgment in the product documentation would be 21 | appreciated but is not required. 22 | 23 | 2. Altered source versions must be plainly marked as such, and must not be 24 | misrepresented as being the original source code. 25 | 26 | 3. This notice may not be removed or altered from any source distribution. 27 | 28 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 29 | 30 | */ 31 | 32 | #include "base64.h" 33 | #include 34 | 35 | static const std::string base64_chars = 36 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 37 | "abcdefghijklmnopqrstuvwxyz" 38 | "0123456789+/"; 39 | 40 | 41 | static inline bool is_base64(unsigned char c) { 42 | return (isalnum(c) || (c == '+') || (c == '/')); 43 | } 44 | 45 | std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { 46 | std::string ret; 47 | int i = 0; 48 | int j = 0; 49 | unsigned char char_array_3[3]; 50 | unsigned char char_array_4[4]; 51 | 52 | while (in_len--) { 53 | char_array_3[i++] = *(bytes_to_encode++); 54 | if (i == 3) { 55 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 56 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 57 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 58 | char_array_4[3] = char_array_3[2] & 0x3f; 59 | 60 | for(i = 0; (i <4) ; i++) 61 | ret += base64_chars[char_array_4[i]]; 62 | i = 0; 63 | } 64 | } 65 | 66 | if (i) 67 | { 68 | for(j = i; j < 3; j++) 69 | char_array_3[j] = '\0'; 70 | 71 | char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2; 72 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 73 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 74 | 75 | for (j = 0; (j < i + 1); j++) 76 | ret += base64_chars[char_array_4[j]]; 77 | 78 | while((i++ < 3)) 79 | ret += '='; 80 | 81 | } 82 | 83 | return ret; 84 | 85 | } 86 | 87 | std::string base64_decode(std::string const& encoded_string) { 88 | int in_len = encoded_string.size(); 89 | int i = 0; 90 | int j = 0; 91 | int in_ = 0; 92 | unsigned char char_array_4[4], char_array_3[3]; 93 | std::string ret; 94 | 95 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 96 | char_array_4[i++] = encoded_string[in_]; in_++; 97 | if (i ==4) { 98 | for (i = 0; i <4; i++) 99 | char_array_4[i] = base64_chars.find(char_array_4[i]); 100 | 101 | char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4); 102 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 103 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 104 | 105 | for (i = 0; (i < 3); i++) 106 | ret += char_array_3[i]; 107 | i = 0; 108 | } 109 | } 110 | 111 | if (i) { 112 | for (j = 0; j < i; j++) 113 | char_array_4[j] = base64_chars.find(char_array_4[j]); 114 | 115 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 116 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 117 | 118 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 119 | } 120 | 121 | return ret; 122 | } 123 | -------------------------------------------------------------------------------- /c_src/base64.h: -------------------------------------------------------------------------------- 1 | // 2 | // base64 encoding and decoding with C++. 3 | // Version: 1.01.00 4 | // 5 | 6 | #ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 7 | #define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A 8 | 9 | #include 10 | 11 | std::string base64_encode(unsigned char const* , unsigned int len); 12 | std::string base64_decode(std::string const& s); 13 | 14 | #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ -------------------------------------------------------------------------------- /c_src/erl_signal_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | #include "libsignal-c/signal_protocol.h" 7 | #include "libsignal-c/signal_protocol_types.h" 8 | #include "erl_signal_client_storage.h" 9 | 10 | typedef struct esc_mutexes { 11 | #ifndef NO_THREADS 12 | pthread_mutex_t * mutex_p; 13 | pthread_mutexattr_t * mutex_attr_p; 14 | #endif 15 | } esc_mutexes; 16 | 17 | typedef signal_buffer esc_buf; 18 | typedef signal_protocol_address esc_address; 19 | 20 | struct esc_context { 21 | signal_context * global_context_p = NULL; 22 | signal_protocol_store_context * store_context_p = NULL; 23 | esc_mutexes * mutexes_p = NULL; 24 | esc_storage * session_store = NULL; 25 | esc_storage * pre_key_store = NULL; 26 | esc_storage * signed_pre_key_store = NULL; 27 | esc_storage * identity_key_store = NULL; 28 | esc_storage * settings = NULL; 29 | void (*log_func)(int level, const char * message, size_t len, void * user_data); 30 | int log_level; 31 | }; 32 | 33 | struct esc_handshake { 34 | session_builder * session_builder_p = NULL; 35 | esc_buf * handshake_msg_p = NULL; 36 | }; 37 | 38 | struct esc_buf_list_item { 39 | uint32_t id; 40 | esc_buf * buf_p = NULL; 41 | esc_buf_list_item * next_p = NULL; 42 | }; 43 | 44 | struct esc_bundle { 45 | uint32_t registration_id; 46 | esc_buf_list_item * pre_keys_head_p = NULL; 47 | uint32_t signed_pre_key_id; 48 | esc_buf * signed_pre_key_public_serialized_p = NULL; 49 | esc_buf * signed_pre_key_signature_p = NULL; 50 | esc_buf * identity_key_public_serialized_p = NULL; 51 | }; 52 | 53 | #define ESC_LOG_ERROR 0 54 | #define ESC_LOG_WARNING 1 55 | #define ESC_LOG_NOTICE 2 56 | #define ESC_LOG_INFO 3 57 | #define ESC_LOG_DEBUG 4 58 | 59 | #define ESC_ERR -10000 60 | #define ESC_ERR_NOMEM -10001 61 | #define ESC_ERR_NOT_A_PREKEY_MSG -10100 62 | #define ESC_ERR_INVALID_KEY_ID -10200 63 | 64 | #define ESC_DB_DEFAULT_FN "axc.sqlite" 65 | #define ESC_PRE_KEYS_AMOUNT 100 66 | 67 | /** 68 | * Allocates the axc context. 69 | * 70 | * @param ctx_pp Will point to the created context. 71 | * @return 0 on success, negative on failure 72 | */ 73 | int esc_context_create(esc_context ** ctx_pp); 74 | 75 | /** 76 | * Sets the filename/location of the db. 77 | * Should be done after creating the context, but before calling esc_init(). 78 | * 79 | * @param ctx_p The fresh axc context. 80 | * @param filename The filename/path to be used. 81 | * @param fn_len Length of the filename. 82 | * @return 0 on success, negative on failure 83 | */ 84 | int esc_context_set_db_fn(esc_context * ctx_p, char * filename, size_t fn_len); 85 | 86 | /** 87 | * Returns the filename to be used for the database. 88 | * 89 | * @param ctx_p The axc context. 90 | * @return The filename set via esc_context_set_db_fn(), or ESC_DEFAULT_DB_FN if none was set. 91 | */ 92 | const char * esc_context_get_db_fn(esc_context * ctx_p); 93 | 94 | void esc_context_set_log_func(esc_context * ctx_p, void (*log_func)(int level, const char * message, size_t len, void * user_data)); 95 | void esc_context_set_log_level(esc_context * ctx_p, int level); 96 | int esc_context_get_log_level(esc_context * ctx_p); 97 | 98 | void esc_context_destroy_all(esc_context * ctx_p); 99 | signal_context * esc_context_get_axolotl_ctx(esc_context * ctx_p); 100 | 101 | void esc_default_log(int level, const char *message, size_t len, void *user_data); 102 | void esc_log(esc_context * ctx_p, int level, const char * format, ...); 103 | 104 | int esc_buf_list_item_create(esc_buf_list_item ** item_pp, uint32_t * id_p, esc_buf * data_p); 105 | void esc_buf_list_item_set_next(esc_buf_list_item * item_p, esc_buf_list_item * next_p); 106 | esc_buf_list_item * esc_buf_list_item_get_next(esc_buf_list_item * item_p); 107 | uint32_t esc_buf_list_item_get_id(esc_buf_list_item * item_p); 108 | esc_buf * esc_buf_list_item_get_buf(esc_buf_list_item * item_p); 109 | void esc_buf_list_free(esc_buf_list_item * head_p); 110 | 111 | /** 112 | * Collects the info needed to publish a bundle. 113 | * 114 | * @param n Number of pre keys to get. 115 | * @param ctx_p Pointer to the initialized axc context. 116 | * @param bundle_pp Will be set to the bundle. 117 | * @return 0 on success, negative on error. 118 | */ 119 | int esc_bundle_collect(size_t n, esc_context * ctx_p, esc_bundle ** bundle_pp); 120 | uint32_t esc_bundle_get_reg_id(esc_bundle * bundle_p); 121 | esc_buf_list_item * esc_bundle_get_pre_key_list(esc_bundle * bundle_p); 122 | uint32_t esc_bundle_get_signed_pre_key_id(esc_bundle * bundle_p); 123 | esc_buf * esc_bundle_get_signed_pre_key(esc_bundle * bundle_p); 124 | esc_buf * esc_bundle_get_signature(esc_bundle * bundle_p); 125 | esc_buf * esc_bundle_get_identity_key(esc_bundle * bundle_p); 126 | void esc_bundle_destroy(esc_bundle * bundle_p); 127 | 128 | /** 129 | * Initializes the library. Has to be called at every startup. 130 | * 131 | * @param ctx_p A pointer to an already created axc context. 132 | * @return 0 on success, negative on failure 133 | */ 134 | int esc_init(esc_context * ctx_p); 135 | 136 | /** 137 | * Generates identity keys and store them in internal storage 138 | * 139 | * @param ctx_p A pointer to an already created axc context. 140 | * @return 0 on success, negative on failure 141 | */ 142 | const char * esc_generate_identity_keys(esc_context * ctx_p); 143 | 144 | /** 145 | * Destroys mutexes and axolotl contexts saved in the axc context. 146 | * 147 | * @param ctx_p Pointer to the axc context as received from esc_init(). 148 | */ 149 | void esc_cleanup(esc_context * ctx_p); 150 | 151 | /** 152 | * "Installs" the library by creating the database and saving the necessary encryption keys into it. 153 | * Needs to be called once at the beginning, but can be called at every startup as it will not touch an initialized database. 154 | * 155 | * @param ctx_p Pointer to the axc context as received from esc_init(). 156 | * @return 0 on success, negative on failure 157 | */ 158 | int esc_install(esc_context * ctx_p); 159 | 160 | /** 161 | * Retrieves the local registration ID. 162 | * 163 | * @param ctx_p Pointer to an initialized and installed esc_context. 164 | * @param id_p Will be set to the ID. 165 | * @return 0 on success, negative on error. 166 | */ 167 | int esc_get_device_id(esc_context * ctx_p, uint32_t * id_p); 168 | 169 | esc_buf * esc_buf_create(const uint8_t * data, size_t len); 170 | unsigned char * esc_buf_get_data(esc_buf * buf); 171 | size_t esc_buf_get_len(esc_buf * buf); 172 | void esc_buf_free(esc_buf * buf); 173 | 174 | esc_buf * esc_handshake_get_data(esc_handshake * handshake_p); 175 | 176 | /** 177 | * Generates the message that is needed to initiate a session synchronously, which internally is axolotl's key_exchange_message. 178 | * The returned esc_handshake has to be kept and given to esc_handshake_acknowledge(), together with the received response. 179 | * At the end, it should be freed by calling esc_handshake_destroy(). 180 | * 181 | * The whole key exchange process looks like this: 182 | * A initiate 183 | * A -> B 184 | * B accept 185 | * B -> A 186 | * A acknowledge 187 | * 188 | * @param recipient_addr_p The address of the recipient. 189 | * @param ctx_p The client context. 190 | * @param handshake_pp The handshake struct to keep for the next steps. 191 | * @return 0 on success, negative on error 192 | */ 193 | const char * esc_handshake_initiate(esc_address *sender_addr_p, esc_address *recipient_addr_p, esc_context * ctx_p, session_cipher **cipher, session_builder **builder, esc_buf **response); 194 | 195 | /** 196 | * Second step of the session establishment, is called by the recipient. 197 | * 198 | * @param msg_data_p A pointer to a buffer with the raw message data. 199 | * @param sender_addr_p A pointer to an address struct with the sender's information. 200 | * @param ctx_p The axc context. 201 | * @param handshake_response_pp Will point to the response message if successful, unset otherwise. Has to be freed using esc_handshake_destroy(). 202 | * @return 0 on success, negative on error. 203 | */ 204 | const char * esc_handshake_accept(esc_buf * msg_data_p, esc_address * sender_addr_p, esc_context * ctx_p, session_cipher **cipher, session_builder **builder, esc_address ** address_from_p, esc_buf **response); 205 | 206 | /** 207 | * Third and final step of session establishment. 208 | * 209 | * @param msg_data_p A pointer to a buffer containing the raw message data. 210 | * @param handshake_p Pointer to the esc_handshake returned by esc_handshake_initiate(). Should be freed by esc_handshake_destroy() afterwards. 211 | * @param ctx_p The axc context. 212 | * @return 0 on success, negative on error. 213 | * 214 | */ 215 | const char * esc_handshake_acknowledge(esc_buf * msg_data_p, esc_address *address, esc_context * ctx_p, session_cipher **cipher, esc_address **address_from_p); 216 | 217 | /** 218 | * Frees the memory used by this struct and its members. 219 | * 220 | * @param The esc_handshake to destroy. 221 | */ 222 | //void esc_handshake_destroy(esc_handshake * hs); 223 | 224 | /** 225 | * Encrypts a message. Needs an established session, either synchronous or built from bundle. 226 | * The buffer containing the ciphertext has to be freed afterwards. 227 | * 228 | * If data is a string, include the null terminator in the data. 229 | * 230 | * @param msg_p The data to encrypt. 231 | * @param recipient_addr_p Address of the recipient. 232 | * @param ctx_p The axc context. 233 | * @param ciphertext_pp Will point to the serialized ciphertext afterwards. 234 | * @return 0 on success, negative on error. 235 | */ 236 | const char * esc_message_encrypt_and_serialize(esc_buf * msg_p, const esc_address * recipient_addr_p, esc_context * ctx_p, esc_buf ** ciphertext_pp); 237 | 238 | /** 239 | * Decrypts a received message. Needs an established session. 240 | * 241 | * As the null terminator should be included in the data bytes to be encrypted in case of a string, 242 | * the data of the esc_buf should also work as a string after decryption. 243 | * 244 | * @param msg_p The data to decrypt. 245 | * @param sender_addr_p Address of the sender. 246 | * @param ctx_p The axc context. 247 | * @param plaintext_pp Will point to the plaintext afterwards. Has to be freed. 248 | * @return 0 on success, negative on error. 249 | */ 250 | const char * esc_message_decrypt_from_serialized (esc_buf * msg_p, esc_address * sender_addr_p, esc_context * ctx_p, esc_buf ** plaintext_pp); 251 | 252 | /** 253 | * Checks if an initiated session exists (and no pending synchronous handshake). 254 | * 255 | * @param addr_p The address for which to check if a session exists. 256 | * @param ctx_p The axc context. 257 | * @return 1 if it exists, 0 if it does not, negative on error 258 | */ 259 | int esc_session_exists_initiated(const esc_address * addr_p, esc_context * ctx_p); 260 | 261 | /** 262 | * Checks if there exists a session for a user. 263 | * 264 | * @param name The username. 265 | * @param ctx_p Pointer to the axc context. 266 | * @return 1 if at least one session exists, 0 if no session exists, negative on error. 267 | */ 268 | int esc_session_exists_any(const char * name, esc_context * ctx_p); 269 | 270 | /** 271 | * Creates a session from a fetched bundle which can then instantly be used to encrypt a message. 272 | * 273 | * @param pre_key_id The ID of the used prekey. 274 | * @param pre_key_public_serialized_p Pointer to a buffer containing the serialized public part of the pre key pair. 275 | * @param signed_pre_key_id The ID of the signed prekey. 276 | * @param signed_pre_key_public_serialized_p Pointer to a buffer containing the serialized public part of the signed pre key pair. 277 | * @param signed_pre_key_signature_p Pointer to a buffer containing the signature data of the signed pre key. 278 | * @param identity_key_public_serialized_p Pointer to a buffer containing the serialized public part of the identity key pair. 279 | * @param remote_address_p Pointer to the address of the recipient. 280 | * @param ctx_p Pointer to the esc_context. 281 | * @return 0 on success, negative on error. 282 | */ 283 | int esc_session_from_bundle(uint32_t pre_key_id, 284 | esc_buf * pre_key_public_serialized_p, 285 | uint32_t signed_pre_key_id, 286 | esc_buf * signed_pre_key_public_serialized_p, 287 | esc_buf * signed_pre_key_signature_p, 288 | esc_buf * identity_key_public_serialized_p, 289 | const esc_address * remote_address_p, 290 | esc_context * ctx_p); 291 | 292 | /** 293 | * Deletes a session for a user:device combination. 294 | * 295 | * @param user Username. 296 | * @param device_id The device ID. 297 | * @param ctx_p Pointer to the axc context. 298 | * @return 0 on success, negative on error. 299 | */ 300 | int esc_session_delete(const char * user, uint32_t device_id, esc_context * ctx_p); 301 | 302 | /** 303 | * Creates a session from a received pre key message and uses it to decrypt the actual message body.. 304 | * The ciphertext is decrypted here to avoid reserializing the message or having to deal with internal axolotl data structures. 305 | * 306 | * @param pre_key_msg_serialized_p Pointer to the buffer containing the serialized message. 307 | * @param remote_address_p Pointer to the remote (sender) address. 308 | * @param ctx_p Pointer to the axc context. 309 | * @param msg_pp Will contain a pointer to the decrypted plaintext. 310 | * @return 0 on success, negative on error 311 | */ 312 | int esc_pre_key_message_process(esc_buf * pre_key_msg_serialized_p, esc_address * remote_address_p, esc_context * ctx_p, esc_buf ** plaintext_pp); 313 | 314 | /** 315 | * Retrieves the own public identity key. 316 | * 317 | * @param ctx_p Pointer to the esc_context. 318 | * @param pubkey_data_pp Will point to an esc_buf * containing the serialized key data. 319 | * @return 0 on success, negative on error. 320 | */ 321 | int esc_key_load_public_own(esc_context * ctx_p, esc_buf ** pubkey_data_pp); 322 | 323 | /** 324 | * Retrieves the serialized public identity key for a user's device. 325 | * 326 | * @param name The user's name. 327 | * @param device_id The device's ID. 328 | * @param ctx_p Pointer to the esc_context. 329 | * @param pubkey_data_pp Will point to an esc_buf * which contains the data. 330 | * @return 1 if the key was loaded, 0 if no session exists, negative on error. 331 | */ 332 | int esc_key_load_public_addr(const char * name, uint32_t device_id, esc_context * ctx_p, esc_buf ** pubkey_data_pp); 333 | -------------------------------------------------------------------------------- /c_src/erl_signal_client_storage.cpp: -------------------------------------------------------------------------------- 1 | #include "erl_signal_client_storage.h" 2 | #include "base64.h" 3 | 4 | 5 | /** 6 | * STORAGE 7 | **/ 8 | 9 | esc_storage::esc_storage() { 10 | 11 | } 12 | 13 | esc_storage::~esc_storage() { 14 | 15 | } 16 | 17 | void esc_storage::set(const key key, const row row ) { 18 | esc_storage::storage::iterator iterator = this->data.find(key); 19 | if (iterator == this->data.end()) { 20 | this->data[key] = row; 21 | } else { 22 | iterator->second = row; 23 | } 24 | this->is_changed = true; 25 | }; 26 | 27 | void esc_storage::erase(const key key) { 28 | this->data.erase(key); 29 | this->is_changed = true; 30 | }; 31 | 32 | const esc_storage::row esc_storage::get(const key key) const { 33 | esc_storage::storage::const_iterator iterator = this->data.find(key); 34 | if (iterator == this->data.end()) { 35 | return row(); 36 | } else { 37 | return iterator->second; 38 | } 39 | }; 40 | 41 | esc_storage::storage::const_iterator esc_storage::begin() const{ 42 | return this->data.begin(); 43 | } 44 | 45 | esc_storage::storage::const_iterator esc_storage::end() const { 46 | return this->data.end(); 47 | } 48 | 49 | void esc_storage::flush() { 50 | this->is_changed = false; 51 | } 52 | 53 | std::list esc_storage::get_nearby(const std::string key) const { 54 | std::list result; 55 | storage::const_iterator iterator = this->data.lower_bound(key); 56 | int n = key.size(); 57 | bool flag = true; 58 | do { 59 | std::string k = iterator->first; 60 | k.resize(n, 0); 61 | if (key.compare(k) == 0) { 62 | result.push_front(iterator->second); 63 | iterator++; 64 | } else { 65 | flag = false; 66 | } 67 | } while (flag); 68 | return result; 69 | } 70 | 71 | int esc_storage::erase_nearby(const key key) { 72 | storage::const_iterator iterator = this->data.lower_bound(key); 73 | int n = key.size(); 74 | bool flag = true; 75 | int counter = 0; 76 | do { 77 | std::string k = iterator->first; 78 | k.resize(n, 0); 79 | if (key.compare(k) == 0) { 80 | iterator = this->data.erase(iterator); 81 | counter++; 82 | } else { 83 | flag = false; 84 | } 85 | } while (flag || iterator == this->data.end()); 86 | this->is_changed = true; 87 | return counter; 88 | } 89 | 90 | void esc_storage::clear() { 91 | this->data.clear(); 92 | this->is_changed = true; 93 | } 94 | 95 | bool esc_storage::is_member(const key key) const { 96 | return this->data.find(key) != this->data.end(); 97 | } 98 | 99 | std::list esc_storage::keys() const{ 100 | std::list result; 101 | for(storage::const_iterator it=this->data.begin();it != this->data.end();it++) { 102 | result.push_front(it->first); 103 | } 104 | return result; 105 | } 106 | 107 | 108 | ERL_NIF_TERM to_binary(ErlNifEnv*env, const std::string &str) { 109 | 110 | ERL_NIF_TERM result; 111 | ErlNifBinary binary; 112 | enif_alloc_binary(str.size(), &binary); 113 | memcpy((char *) binary.data, (char *) str.c_str(), str.size()); 114 | result = enif_make_binary(env, &binary); 115 | return result; 116 | 117 | } 118 | 119 | ERL_NIF_TERM esc_storage::serialize(ErlNifEnv* env) const { 120 | ERL_NIF_TERM tail = enif_make_list(env, 0); 121 | for(storage::const_iterator it=this->data.begin();it != this->data.end();it++) { 122 | tail = enif_make_list_cell(env, 123 | enif_make_tuple2(env, 124 | to_binary(env, it->first), 125 | it->second.serialize(env) 126 | ), tail); 127 | } 128 | return tail; 129 | } 130 | /* 131 | std::string esc_storage::serialize() { 132 | cJSON *json = cJSON_CreateObject(); 133 | for(esc_storage::storage::const_iterator iterator = this->data.begin();iterator != this->data.end(); iterator++) { 134 | key key = base64_encode((const unsigned char *) iterator->first.c_str(), iterator->first.length()); 135 | row value = base64_encode((const unsigned char *) iterator->second.c_str(), iterator->second.length()); 136 | cJSON_AddStringToObject(json, key.c_str(), value.c_str()); 137 | } 138 | std::string result = cJSON_Print(json); 139 | cJSON_Delete(json); 140 | return result; 141 | }; 142 | 143 | int esc_storage::unserialize(const std::string str) { 144 | cJSON *json_begin = cJSON_Parse(str.c_str()); 145 | this->data.clear(); 146 | for(cJSON *json = json_begin; json != NULL; json = json->next) { 147 | std::string key = base64_decode(std::string(json->string)); 148 | std::string value = base64_decode(std::string(json->valuestring)); 149 | this->data.insert(key, value); 150 | } 151 | this->is_changed = false; 152 | cJSON_Delete(json_begin); 153 | return 0; 154 | }; 155 | */ 156 | 157 | 158 | /** 159 | * ROW 160 | **/ 161 | void esc_storage::row::store(const column column, const value value) { 162 | row_map::iterator iterator = this->data.find(column); 163 | if (iterator == this->data.end()) { 164 | this->data[column] = value; 165 | } else { 166 | delete &iterator->second; 167 | iterator->second = value; 168 | } 169 | }; 170 | 171 | int esc_storage::size() const { 172 | return this->data.size(); 173 | } 174 | 175 | 176 | const esc_storage::value esc_storage::row::get(const column column, const value default_value) const{ 177 | row_map::const_iterator iterator = this->data.find(column); 178 | if (iterator == this->data.end()) { 179 | return default_value; 180 | } else { 181 | return iterator->second; 182 | } 183 | }; 184 | 185 | bool esc_storage::row::is_empty() const { 186 | return this->data.empty(); 187 | }; 188 | 189 | esc_storage::row::row_map::const_iterator esc_storage::row::begin() const { 190 | return this->data.begin(); 191 | } 192 | 193 | esc_storage::row::row_map::const_iterator esc_storage::row::end() const { 194 | return this->data.end(); 195 | } 196 | 197 | ERL_NIF_TERM esc_storage::row::serialize(ErlNifEnv* env) const { 198 | ERL_NIF_TERM tail = enif_make_list(env, 0); 199 | for(row_map::const_iterator it=this->data.begin();it != this->data.end();it++) { 200 | tail = enif_make_list_cell(env, 201 | enif_make_tuple2(env, 202 | to_binary(env, it->first), 203 | to_binary(env, it->second) 204 | ), tail); 205 | } 206 | return tail; 207 | } -------------------------------------------------------------------------------- /c_src/erl_signal_client_storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | 11 | class esc_storage { 12 | public: 13 | 14 | typedef std::string column; 15 | typedef std::string value; 16 | 17 | class row { 18 | public: 19 | typedef std::map row_map; 20 | private: 21 | row_map data; 22 | public: 23 | bool is_empty() const; 24 | void store(const column column, const value value); 25 | const value get(const column column, const value default_value) const; 26 | row_map::const_iterator begin() const; 27 | row_map::const_iterator end() const; 28 | ERL_NIF_TERM serialize(ErlNifEnv* env) const; 29 | }; 30 | 31 | typedef std::string key; 32 | typedef std::map storage; 33 | 34 | private: 35 | storage data; 36 | protected: 37 | bool is_changed = false; 38 | public: 39 | esc_storage(); 40 | ~esc_storage(); 41 | void set(const key key, const row row ); 42 | const row get(const key key) const; 43 | bool is_member(const key key) const; 44 | void erase(const key key); 45 | storage::const_iterator begin() const; 46 | storage::const_iterator end() const; 47 | void flush(); 48 | std::list get_nearby(const key key) const; 49 | int erase_nearby(const key key); 50 | void clear(); 51 | std::list keys() const; 52 | int size() const; 53 | ERL_NIF_TERM serialize(ErlNifEnv* env) const; 54 | //int unserialize(const std::string str); 55 | }; 56 | -------------------------------------------------------------------------------- /c_src/erl_signal_crypto.cpp: -------------------------------------------------------------------------------- 1 | #include // int types 2 | #include // fprintf 3 | #include // malloc 4 | 5 | #include 6 | 7 | #include "libsignal-c/signal_protocol.h" 8 | 9 | #include "erl_signal_client.h" 10 | 11 | void esc_crypto_init(void) { 12 | (void) gcry_check_version(NULL); 13 | gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN); 14 | gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); 15 | gcry_control (GCRYCTL_RESUME_SECMEM_WARN); 16 | gcry_control(GCRYCTL_USE_SECURE_RNDPOOL); 17 | gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 18 | } 19 | 20 | void esc_crypto_teardown(void) { 21 | } 22 | 23 | int random_bytes(uint8_t * data_p, size_t len, void * user_data_p) { 24 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 25 | (void) esc_ctx_p; 26 | 27 | gcry_randomize(data_p, len, GCRY_STRONG_RANDOM); 28 | 29 | return SG_SUCCESS; 30 | } 31 | 32 | int hmac_sha256_init(void ** hmac_context_pp, const uint8_t * key_p, size_t key_len, void * user_data_p) { 33 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 34 | int ret_val = 0; 35 | const char * err_msg = NULL; 36 | 37 | gcry_mac_hd_t * hmac_hd_p = NULL; 38 | 39 | hmac_hd_p = new gcry_mac_hd_t(); 40 | if (!hmac_hd_p) { 41 | err_msg = "could not malloc hmac-sha256 ctx"; 42 | ret_val = SG_ERR_NOMEM; 43 | goto cleanup; 44 | } 45 | 46 | ret_val = gcry_mac_open(hmac_hd_p, GCRY_MAC_HMAC_SHA256, 0, NULL); 47 | if (ret_val) { 48 | err_msg = "could not create hmac-sha256 ctx"; 49 | goto cleanup; 50 | } 51 | 52 | ret_val = gcry_mac_setkey(*hmac_hd_p, key_p, key_len); 53 | if (ret_val) { 54 | err_msg = "could not set key for hmac"; 55 | goto cleanup; 56 | } 57 | 58 | *hmac_context_pp = hmac_hd_p; 59 | 60 | cleanup: 61 | if (ret_val) { 62 | if (ret_val > 0) { 63 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 64 | ret_val = SG_ERR_UNKNOWN; 65 | } else { 66 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 67 | } 68 | 69 | if (hmac_hd_p) { 70 | gcry_mac_close(*hmac_hd_p); 71 | free(hmac_hd_p); 72 | } 73 | } 74 | return ret_val; 75 | } 76 | 77 | int hmac_sha256_update(void * hmac_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p) { 78 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 79 | (void) esc_ctx_p; 80 | 81 | gcry_mac_write(*((gcry_mac_hd_t *) hmac_context_p), data_p, data_len); 82 | 83 | return SG_SUCCESS; 84 | } 85 | 86 | int hmac_sha256_final(void * hmac_context_p, signal_buffer ** output_pp, void * user_data_p) { 87 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 88 | int ret_val = 0; 89 | const char * err_msg = NULL; 90 | 91 | int algo = GCRY_MAC_HMAC_SHA256; 92 | size_t mac_len = 0; 93 | uint8_t * mac_data_p = NULL; 94 | signal_buffer * out_buf_p = NULL; 95 | 96 | mac_len = gcry_mac_get_algo_maclen(algo); 97 | 98 | mac_data_p = new uint8_t[mac_len]; 99 | if (!mac_data_p) { 100 | ret_val = SG_ERR_NOMEM; 101 | err_msg = "failed to malloc mac buf"; 102 | goto cleanup; 103 | } 104 | 105 | ret_val = gcry_mac_read(*((gcry_mac_hd_t *) hmac_context_p), mac_data_p, &mac_len); 106 | if (ret_val) { 107 | err_msg = "failed to read mac"; 108 | goto cleanup; 109 | } 110 | 111 | out_buf_p = signal_buffer_create(mac_data_p, mac_len); 112 | if (!out_buf_p) { 113 | ret_val = SG_ERR_NOMEM; 114 | err_msg = "failed to create mac output buf"; 115 | goto cleanup; 116 | } 117 | 118 | *output_pp = out_buf_p; 119 | 120 | cleanup: 121 | if (ret_val) { 122 | if (ret_val > 0) { 123 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 124 | ret_val = SG_ERR_UNKNOWN; 125 | } else { 126 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 127 | } 128 | } 129 | free(mac_data_p); 130 | 131 | return ret_val; 132 | } 133 | 134 | void hmac_sha256_cleanup(void * hmac_context_p, void * user_data_p) { 135 | (void) user_data_p; 136 | 137 | gcry_mac_hd_t * mac_hd_p = (gcry_mac_hd_t *) hmac_context_p; 138 | 139 | gcry_mac_close(*mac_hd_p); 140 | free(mac_hd_p); 141 | } 142 | 143 | int sha512_digest_init(void ** digest_context_pp, void * user_data_p) { 144 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 145 | int ret_val = 0; 146 | const char * err_msg = NULL; 147 | 148 | gcry_md_hd_t * hash_hd_p = NULL; 149 | hash_hd_p = new gcry_md_hd_t(); 150 | if (!hash_hd_p) { 151 | err_msg = "could not malloc sha512 ctx"; 152 | ret_val = SG_ERR_NOMEM; 153 | goto cleanup; 154 | } 155 | 156 | ret_val = gcry_md_open(hash_hd_p, GCRY_MD_SHA512, 0); 157 | if (ret_val) { 158 | err_msg = "could not create sha512 ctx"; 159 | goto cleanup; 160 | } 161 | 162 | *digest_context_pp = hash_hd_p; 163 | 164 | cleanup: 165 | if (ret_val) { 166 | if (ret_val > 0) { 167 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 168 | ret_val = SG_ERR_UNKNOWN; 169 | } else { 170 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 171 | } 172 | 173 | if (hash_hd_p) { 174 | gcry_md_close(*hash_hd_p); 175 | free(hash_hd_p); 176 | } 177 | } 178 | 179 | return ret_val; 180 | } 181 | 182 | int sha512_digest_update(void * digest_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p) { 183 | (void) user_data_p; 184 | 185 | gcry_md_write(*((gcry_md_hd_t *) digest_context_p), data_p, data_len); 186 | 187 | return 0; 188 | } 189 | 190 | int sha512_digest_final(void * digest_context_p, signal_buffer ** output_pp, void * user_data_p) { 191 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 192 | gcry_md_hd_t * hash_hd_p = (gcry_md_hd_t *) digest_context_p; 193 | int ret_val = 0; 194 | const char * err_msg = NULL; 195 | 196 | int algo = GCRY_MD_SHA512; 197 | size_t hash_len = 0; 198 | unsigned char * hash_data_p = NULL; 199 | signal_buffer * out_buf_p = NULL; 200 | 201 | hash_len = gcry_md_get_algo_dlen(algo); 202 | 203 | hash_data_p = gcry_md_read(*hash_hd_p, algo); 204 | if (!hash_data_p) { 205 | ret_val = SG_ERR_UNKNOWN; 206 | err_msg = "failed to read hash"; 207 | goto cleanup; 208 | } 209 | 210 | out_buf_p = signal_buffer_create((uint8_t *) hash_data_p, hash_len); 211 | if (!out_buf_p) { 212 | ret_val = SG_ERR_NOMEM; 213 | err_msg = "failed to create hash output buf"; 214 | goto cleanup; 215 | } 216 | 217 | gcry_md_reset(*hash_hd_p); 218 | 219 | *output_pp = out_buf_p; 220 | 221 | cleanup: 222 | if (ret_val) { 223 | if (ret_val > 0) { 224 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 225 | ret_val = SG_ERR_UNKNOWN; 226 | } else { 227 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 228 | } 229 | } 230 | 231 | return ret_val; 232 | } 233 | 234 | void sha512_digest_cleanup(void * digest_context_p, void * user_data_p) { 235 | (void) user_data_p; 236 | 237 | gcry_md_hd_t * hash_hd_p = (gcry_md_hd_t *) digest_context_p; 238 | 239 | gcry_md_close(*hash_hd_p); 240 | free(hash_hd_p); 241 | } 242 | 243 | static int choose_aes(int cipher, size_t key_len, int * algo_p, int * mode_p) { 244 | int algo = 0; 245 | int mode = 0; 246 | 247 | switch(key_len) { 248 | case 16: 249 | algo = GCRY_CIPHER_AES128; 250 | break; 251 | case 24: 252 | algo = GCRY_CIPHER_AES192; 253 | break; 254 | case 32: 255 | algo = GCRY_CIPHER_AES256; 256 | break; 257 | default: 258 | return SG_ERR_UNKNOWN; 259 | } 260 | 261 | switch (cipher) { 262 | case SG_CIPHER_AES_CBC_PKCS5: 263 | mode = GCRY_CIPHER_MODE_CBC; 264 | break; 265 | case SG_CIPHER_AES_CTR_NOPADDING: 266 | mode = GCRY_CIPHER_MODE_CTR; 267 | break; 268 | default: 269 | return SG_ERR_UNKNOWN; 270 | } 271 | 272 | *algo_p = algo; 273 | *mode_p = mode; 274 | 275 | return 0; 276 | } 277 | 278 | int aes_encrypt(signal_buffer ** output_pp, 279 | int cipher, 280 | const uint8_t * key_p, size_t key_len, 281 | const uint8_t * iv_p, size_t iv_len, 282 | const uint8_t * plaintext_p, size_t plaintext_len, 283 | void * user_data_p) { 284 | 285 | int ret_val = SG_SUCCESS; 286 | const char * err_msg = NULL; 287 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 288 | 289 | int algo = 0; 290 | int mode = 0; 291 | size_t pad_len = 0; 292 | size_t ct_len = 0; 293 | gcry_cipher_hd_t cipher_hd = {0}; 294 | uint8_t * pt_p = NULL; 295 | uint8_t * out_p = NULL; 296 | signal_buffer * out_buf_p = NULL; 297 | 298 | if(iv_len != 16) { 299 | err_msg = "invalid AES IV size (must be 16)"; 300 | ret_val = SG_ERR_UNKNOWN; 301 | goto cleanup; 302 | } 303 | 304 | ret_val = choose_aes(cipher, key_len, &algo, &mode); 305 | if (ret_val) { 306 | err_msg = "failed to choose cipher"; 307 | ret_val = SG_ERR_UNKNOWN; 308 | goto cleanup; 309 | } 310 | 311 | ret_val = gcry_cipher_open(&cipher_hd, algo, mode, 0); 312 | if (ret_val) { 313 | err_msg = "failed to init cipher"; 314 | goto cleanup; 315 | } 316 | 317 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 318 | if (ret_val) { 319 | err_msg = "failed to set key"; 320 | goto cleanup; 321 | } 322 | 323 | switch (cipher) { 324 | case SG_CIPHER_AES_CBC_PKCS5: 325 | pad_len = 16 - (plaintext_len % 16); 326 | if (pad_len == 0) { 327 | pad_len = 16; 328 | } 329 | ct_len = plaintext_len + pad_len; 330 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 331 | if (ret_val) { 332 | err_msg = "failed to set iv"; 333 | goto cleanup; 334 | } 335 | break; 336 | case SG_CIPHER_AES_CTR_NOPADDING: 337 | ct_len = plaintext_len; 338 | ret_val = gcry_cipher_setctr(cipher_hd, iv_p, iv_len); 339 | if (ret_val) { 340 | err_msg = "failed to set iv"; 341 | goto cleanup; 342 | } 343 | break; 344 | default: 345 | ret_val = SG_ERR_UNKNOWN; 346 | err_msg = "unknown cipher"; 347 | goto cleanup; 348 | } 349 | 350 | pt_p = new uint8_t[ct_len]; 351 | if (!pt_p) { 352 | err_msg = "failed to malloc pt buf"; 353 | ret_val = SG_ERR_NOMEM; 354 | goto cleanup; 355 | } 356 | memset(pt_p, pad_len, ct_len); 357 | memcpy(pt_p, plaintext_p, plaintext_len); 358 | 359 | out_p = new uint8_t[ct_len]; 360 | if (!out_p) { 361 | err_msg = "failed to malloc ct buf"; 362 | ret_val = SG_ERR_NOMEM; 363 | goto cleanup; 364 | } 365 | 366 | ret_val = gcry_cipher_encrypt(cipher_hd, out_p, ct_len, pt_p, ct_len); 367 | if (ret_val) { 368 | err_msg = "failed to encrypt"; 369 | goto cleanup; 370 | } 371 | 372 | out_buf_p = signal_buffer_create(out_p, ct_len); 373 | *output_pp = out_buf_p; 374 | 375 | cleanup: 376 | if (ret_val) { 377 | if (ret_val > 0) { 378 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 379 | ret_val = SG_ERR_UNKNOWN; 380 | } else { 381 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 382 | } 383 | } 384 | 385 | free(out_p); 386 | gcry_cipher_close(cipher_hd); 387 | 388 | return ret_val; 389 | } 390 | 391 | int aes_decrypt(signal_buffer ** output_pp, 392 | int cipher, 393 | const uint8_t * key_p, size_t key_len, 394 | const uint8_t * iv_p, size_t iv_len, 395 | const uint8_t * ciphertext_p, size_t ciphertext_len, 396 | void * user_data_p) { 397 | 398 | int ret_val = SG_SUCCESS; 399 | const char * err_msg = NULL; 400 | esc_context * esc_ctx_p = (esc_context *) user_data_p; 401 | 402 | int algo = 0; 403 | int mode = 0; 404 | gcry_cipher_hd_t cipher_hd = {0}; 405 | uint8_t * out_p = NULL; 406 | size_t pad_len = 0; 407 | signal_buffer * out_buf_p = NULL; 408 | 409 | if(iv_len != 16) { 410 | err_msg = "invalid AES IV size (must be 16)"; 411 | ret_val = SG_ERR_UNKNOWN; 412 | goto cleanup; 413 | } 414 | 415 | ret_val = choose_aes(cipher, key_len, &algo, &mode); 416 | if (ret_val) { 417 | err_msg = "failed to choose cipher"; 418 | ret_val = SG_ERR_UNKNOWN; 419 | goto cleanup; 420 | } 421 | 422 | ret_val = gcry_cipher_open(&cipher_hd, algo, mode, 0); 423 | if (ret_val) { 424 | err_msg = "failed to init cipher"; 425 | goto cleanup; 426 | } 427 | 428 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 429 | if (ret_val) { 430 | err_msg = "failed to set key"; 431 | goto cleanup; 432 | } 433 | 434 | switch (cipher) { 435 | case SG_CIPHER_AES_CBC_PKCS5: 436 | pad_len = 1; 437 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 438 | if (ret_val) { 439 | err_msg = "failed to set iv"; 440 | goto cleanup; 441 | } 442 | break; 443 | case SG_CIPHER_AES_CTR_NOPADDING: 444 | ret_val = gcry_cipher_setctr(cipher_hd, iv_p, iv_len); 445 | if (ret_val) { 446 | err_msg = "failed to set iv"; 447 | goto cleanup; 448 | } 449 | break; 450 | default: 451 | ret_val = SG_ERR_UNKNOWN; 452 | err_msg = "unknown cipher"; 453 | goto cleanup; 454 | } 455 | 456 | out_p = new uint8_t[ciphertext_len]; 457 | if (!out_p) { 458 | err_msg = "failed to malloc pt buf"; 459 | ret_val = SG_ERR_NOMEM; 460 | goto cleanup; 461 | } 462 | 463 | ret_val = gcry_cipher_decrypt(cipher_hd, out_p, ciphertext_len, ciphertext_p, ciphertext_len); 464 | if (ret_val) { 465 | err_msg = "failed to decrypt"; 466 | goto cleanup; 467 | } 468 | 469 | if (pad_len) { 470 | pad_len = out_p[ciphertext_len - 1]; 471 | } 472 | 473 | out_buf_p = signal_buffer_create(out_p, ciphertext_len - pad_len); 474 | *output_pp = out_buf_p; 475 | 476 | 477 | cleanup: 478 | if (ret_val) { 479 | if (ret_val > 0) { 480 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 481 | ret_val = SG_ERR_UNKNOWN; 482 | } else { 483 | esc_log(esc_ctx_p, ESC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 484 | } 485 | } 486 | 487 | free(out_p); 488 | gcry_cipher_close(cipher_hd); 489 | 490 | return ret_val; 491 | } 492 | -------------------------------------------------------------------------------- /c_src/erl_signal_crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // #include "signal_protocol.h" 6 | 7 | void esc_crypto_init(void); 8 | void esc_crypto_teardown(void); 9 | 10 | int random_bytes(uint8_t * data_p, size_t len, void * user_data_p); 11 | 12 | int hmac_sha256_init(void ** hmac_context_pp, const uint8_t * key_p, size_t key_len, void * user_data_p); 13 | int hmac_sha256_update(void * hmac_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p); 14 | int hmac_sha256_final(void * hmac_context_p, signal_buffer ** output_pp, void * user_data_p); 15 | void hmac_sha256_cleanup(void * hmac_context_p, void * user_data_p); 16 | 17 | int sha512_digest_init(void ** digest_context_pp, void * user_data_p); 18 | int sha512_digest_update(void * digest_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p); 19 | int sha512_digest_final(void * digest_context_p, signal_buffer ** output_pp, void * user_data_p); 20 | void sha512_digest_cleanup(void * digest_context_p, void * user_data_p); 21 | 22 | int aes_encrypt(signal_buffer ** output_pp, 23 | int cipher, 24 | const uint8_t * key_p, size_t key_len, 25 | const uint8_t * iv_p, size_t iv_len, 26 | const uint8_t * plaintext_p, size_t plaintext_len, 27 | void * user_data_p); 28 | int aes_decrypt(signal_buffer ** output_pp, 29 | int cipher, 30 | const uint8_t * key_p, size_t key_len, 31 | const uint8_t * iv_p, size_t iv_len, 32 | const uint8_t * ciphertext_p, size_t ciphertext_len, 33 | void * user_data_p); 34 | -------------------------------------------------------------------------------- /c_src/erl_signal_log.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | 5 | #include "erl_signal_log.h" 6 | 7 | void es_log(const char * message) { 8 | /* 9 | if (message != NULL) { 10 | std::cout << message << "\n" << std::flush ; 11 | } 12 | */ 13 | } 14 | 15 | void es_log_hex(const char *message, const char *data, int len) { 16 | /* 17 | if (message != NULL) { 18 | std::cout << message << len << "="; 19 | } 20 | char const hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 21 | 22 | if (data != NULL) { 23 | for(int i=0; i> 4 ]; 26 | std::cout << hex_chars[ ( byte & 0x0F ) >> 0 ]; 27 | std::cout << "|"; 28 | } 29 | std::cout << "\n"; 30 | } 31 | std::cout << std::flush; 32 | */ 33 | } 34 | 35 | -------------------------------------------------------------------------------- /c_src/erl_signal_log.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | void es_log(const char * message); 5 | 6 | void es_log_hex(const char * message, const char * data, int len); 7 | -------------------------------------------------------------------------------- /c_src/erl_signal_nif.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "libsignal-c/signal_protocol.h" 6 | #include "libsignal-c/session_builder.h" 7 | #include "libsignal-c/session_cipher.h" 8 | 9 | #include "erl_signal_client.h" 10 | #include "erl_signal_client_storage.h" 11 | 12 | #include "erl_signal_log.h" 13 | 14 | typedef struct { 15 | esc_context * ctx_p; 16 | } context_resource; 17 | 18 | typedef struct { 19 | session_builder* session_builder_p = NULL; 20 | } session_builder_resource; 21 | 22 | typedef struct { 23 | session_cipher* session_cipher_p = NULL; 24 | } session_cipher_resource; 25 | 26 | extern "C" { 27 | 28 | static ErlNifResourceType* CONTEXT_RESOURCE; 29 | static ErlNifResourceType* SESSION_BUILDER_RESOURCE; 30 | static ErlNifResourceType* SESSION_CIPHER_RESOURCE; 31 | 32 | ERL_NIF_TERM nif_esc_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 33 | ERL_NIF_TERM nif_esc_generate_identity_keys(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 34 | ERL_NIF_TERM nif_esc_is_session_exists_initiated(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 35 | ERL_NIF_TERM nif_esc_handshake_initiate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 36 | ERL_NIF_TERM nif_esc_handshake_accept(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 37 | ERL_NIF_TERM nif_esc_handshake_acknowledge(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 38 | ERL_NIF_TERM nif_esc_encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 39 | ERL_NIF_TERM nif_esc_decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 40 | ERL_NIF_TERM nif_esc_serialize(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 41 | 42 | } 43 | 44 | ERL_NIF_TERM make_response(ErlNifEnv* env, const char * ErrorType, const char * Reason) { 45 | return enif_make_tuple2(env, 46 | enif_make_atom(env, ErrorType), 47 | enif_make_atom(env, Reason) 48 | ); 49 | } 50 | 51 | bool erlnifterm_is_atom(ErlNifEnv* env, ERL_NIF_TERM term, const char * atom) { 52 | return enif_is_identical(term, enif_make_atom(env, atom)); 53 | } 54 | 55 | const char * create_signal_protocol_address(ErlNifEnv* env, const ERL_NIF_TERM es_address, signal_protocol_address *result) { 56 | int arity; 57 | const ERL_NIF_TERM *array = NULL; 58 | 59 | if(!enif_get_tuple(env, es_address, &arity, &array)) { 60 | return "address_is_not_tuple"; 61 | } 62 | 63 | if (arity != 3) { 64 | return "address_tuple_is_bad"; 65 | } 66 | /* 67 | if (erlnifterm_is_atom(env, array[0], "es_address")) { 68 | return "bad_record"; 69 | } 70 | */ 71 | ErlNifBinary buffer; 72 | if (!enif_inspect_binary(env, array[1], &buffer)) { 73 | return "name_is_not_binary"; 74 | } 75 | 76 | unsigned int device_id; 77 | 78 | if (!enif_get_uint(env, array[2], &device_id)) { 79 | return "device_id_is_not_integer"; 80 | } 81 | 82 | result->name = (char *) buffer.data; 83 | result->name_len = buffer.size; 84 | result->device_id = device_id; 85 | 86 | return NULL; 87 | } 88 | 89 | ERL_NIF_TERM construct_es_address_record(ErlNifEnv* env, esc_address *record) { 90 | ERL_NIF_TERM address_bin; 91 | ErlNifBinary binary; 92 | enif_alloc_binary(record->name_len, &binary); 93 | memcpy((char *) binary.data, (char *) record->name, record->name_len); 94 | address_bin = enif_make_binary(env, &binary); 95 | return enif_make_tuple3(env, 96 | enif_make_atom(env, "es_address"), 97 | address_bin, 98 | enif_make_int(env, record->device_id) 99 | ); 100 | } 101 | 102 | ERL_NIF_TERM nif_esc_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 103 | 104 | if (argc != 0 ) { 105 | return enif_make_badarg(env); 106 | } 107 | 108 | context_resource * ctx_res_p = (context_resource*) enif_alloc_resource(CONTEXT_RESOURCE, sizeof(context_resource)); 109 | 110 | if (ctx_res_p == NULL) { 111 | return make_response(env, "error", "cant_create_session"); 112 | } 113 | 114 | int a = esc_context_create(&(ctx_res_p->ctx_p)); 115 | if (a != 0 ) { 116 | return make_response(env, "error", "cant_create_context"); 117 | } 118 | a = esc_init(ctx_res_p->ctx_p); 119 | if (a != 0) { 120 | return make_response(env, "error", "cant_init_context"); 121 | } 122 | 123 | ERL_NIF_TERM result = enif_make_resource(env, ctx_res_p); 124 | //enif_release_resource(ctx_res_p); 125 | es_log(""); 126 | es_log("created new - ok"); 127 | return enif_make_tuple2(env, 128 | enif_make_atom(env, "ok"), 129 | result 130 | ); 131 | } 132 | 133 | ERL_NIF_TERM nif_esc_generate_identity_keys(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 134 | context_resource *ctx_res_p = NULL; 135 | const char * err_msg = NULL; 136 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 137 | return enif_make_badarg(env); 138 | } 139 | err_msg = esc_generate_identity_keys(ctx_res_p->ctx_p); 140 | if (err_msg != NULL) { 141 | return enif_raise_exception(env, make_response(env, "badarg", err_msg)); 142 | } 143 | return enif_make_atom(env, "ok"); 144 | } 145 | 146 | ERL_NIF_TERM nif_esc_is_session_exists_initiated(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 147 | 148 | context_resource *ctx_res_p = NULL; 149 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 150 | return enif_make_badarg(env); 151 | } 152 | 153 | signal_protocol_address *address = new signal_protocol_address(); 154 | 155 | const char * ret_val = create_signal_protocol_address(env, argv[1], address); 156 | if(ret_val != NULL) { 157 | return enif_raise_exception(env, 158 | make_response(env, "error", ret_val) 159 | ); 160 | } 161 | 162 | int r = esc_session_exists_initiated(address, ctx_res_p->ctx_p); 163 | 164 | if (r==0) return enif_make_atom(env, "false"); 165 | if (r==1) return enif_make_atom(env, "true"); 166 | 167 | return enif_raise_exception(env, enif_make_tuple3(env, 168 | enif_make_atom(env, "error"), 169 | enif_make_atom(env, "bad_initialisation_result"), 170 | enif_make_int(env, r) 171 | )); 172 | } 173 | 174 | ERL_NIF_TERM nif_esc_handshake_initiate(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 175 | context_resource *ctx_res_p = NULL; 176 | const char * err_msg; 177 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 178 | return enif_make_badarg(env); 179 | } 180 | 181 | signal_protocol_address sender_address; 182 | const char * ret_val = create_signal_protocol_address(env, argv[1], &sender_address); 183 | if(ret_val != NULL) { 184 | return enif_raise_exception(env, 185 | make_response(env, "error", ret_val) 186 | ); 187 | } 188 | 189 | signal_protocol_address recepient_address; 190 | ret_val = create_signal_protocol_address(env, argv[2], &recepient_address); 191 | if(ret_val != NULL) { 192 | return enif_raise_exception(env, 193 | make_response(env, "error", ret_val) 194 | ); 195 | } 196 | 197 | int result; 198 | session_cipher *cipher; 199 | session_builder *builder; 200 | esc_buf *response; 201 | 202 | err_msg = esc_handshake_initiate(&sender_address, &recepient_address, ctx_res_p->ctx_p, &cipher, &builder, &response); 203 | if (err_msg != NULL) { 204 | return enif_raise_exception(env, make_response(env, "error", err_msg)); 205 | } 206 | 207 | SIGNAL_UNREF(cipher); 208 | SIGNAL_UNREF(builder); 209 | 210 | /* 211 | session_cipher_resource * cipher_res_p = (session_cipher_resource*) enif_alloc_resource(SESSION_CIPHER_RESOURCE, sizeof(session_cipher_resource)); 212 | cipher_res_p->session_cipher_p = cipher; 213 | ERL_NIF_TERM cipher_term = enif_make_resource(env, cipher_res_p); 214 | //enif_release_resource(cipher_res_p); 215 | 216 | session_builder_resource * builder_res_p = (session_builder_resource*) enif_alloc_resource(SESSION_BUILDER_RESOURCE, sizeof(session_cipher_resource)); 217 | builder_res_p->session_builder_p = builder; 218 | ERL_NIF_TERM builder_term = enif_make_resource(env, builder_res_p); 219 | //enif_release_resource(builder_res_p); 220 | 221 | */ 222 | ERL_NIF_TERM response_bin; 223 | ErlNifBinary binary; 224 | enif_alloc_binary(esc_buf_get_len(response), &binary); 225 | memcpy((char *) binary.data, (char *) esc_buf_get_data(response), esc_buf_get_len(response)); 226 | response_bin = enif_make_binary(env, &binary); 227 | 228 | esc_buf_free(response); 229 | //free(esc_buf_get_data(response)); 230 | 231 | return enif_make_tuple2(env, 232 | enif_make_atom(env, "ok"), 233 | // cipher_term, 234 | // builder_term, 235 | response_bin 236 | ); 237 | 238 | // return enif_raise_exception(env, make_response(env, "error", "not_implemented")); 239 | }; 240 | 241 | ERL_NIF_TERM nif_esc_handshake_accept(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 242 | context_resource *ctx_res_p = NULL; 243 | const char * err_msg; 244 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 245 | return enif_raise_exception(env, 246 | make_response(env, "badarg","bad_context") 247 | ); 248 | } 249 | 250 | signal_protocol_address sender_address; 251 | const char * ret_val = create_signal_protocol_address(env, argv[1], &sender_address); 252 | if(ret_val != NULL) { 253 | return enif_raise_exception(env, 254 | make_response(env, "badarg", ret_val) 255 | ); 256 | } 257 | 258 | // es_log_hex("address0: ", sender_address.name, sender_address.name_len); 259 | 260 | ErlNifBinary handshake_bin; 261 | if (!enif_inspect_binary(env, argv[2], &handshake_bin)) { 262 | return make_response(env, "badarg", "handshake"); 263 | } 264 | 265 | esc_buf *buf = esc_buf_create(handshake_bin.data, handshake_bin.size); 266 | session_cipher *cipher = NULL; 267 | session_builder *builder = NULL; 268 | esc_buf *response = NULL; 269 | 270 | esc_address *address_from_p = NULL; 271 | err_msg = esc_handshake_accept(buf, &sender_address, ctx_res_p->ctx_p, &cipher, &builder, &address_from_p, &response); 272 | esc_buf_free(buf); 273 | if (err_msg!=NULL) { 274 | return make_response(env, "error", err_msg); 275 | } 276 | 277 | SIGNAL_UNREF(cipher); 278 | SIGNAL_UNREF(builder); 279 | 280 | /* 281 | session_cipher_resource * cipher_res_p = (session_cipher_resource*) enif_alloc_resource(SESSION_CIPHER_RESOURCE, sizeof(session_cipher_resource)); 282 | cipher_res_p->session_cipher_p = cipher; 283 | ERL_NIF_TERM cipher_term = enif_make_resource(env, cipher_res_p); 284 | //enif_release_resource(cipher_res_p); 285 | 286 | session_builder_resource * builder_res_p = (session_builder_resource*) enif_alloc_resource(SESSION_BUILDER_RESOURCE, sizeof(session_cipher_resource)); 287 | builder_res_p->session_builder_p = builder; 288 | ERL_NIF_TERM builder_term = enif_make_resource(env, builder_res_p); 289 | //enif_release_resource(builder_res_p); 290 | */ 291 | ERL_NIF_TERM response_bin; 292 | ErlNifBinary binary; 293 | enif_alloc_binary(esc_buf_get_len(response), &binary); 294 | memcpy((char *) binary.data, (char *) esc_buf_get_data(response), esc_buf_get_len(response)); 295 | response_bin = enif_make_binary(env, &binary); 296 | 297 | // es_log_hex("handshake: ", (char * ) binary.data, binary.size); 298 | 299 | return enif_make_tuple3(env, 300 | enif_make_atom(env, "ok"), 301 | // cipher_term, 302 | // builder_term, 303 | construct_es_address_record(env, address_from_p), 304 | response_bin 305 | ); 306 | }; 307 | 308 | ERL_NIF_TERM nif_esc_handshake_acknowledge(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 309 | const char * err_msg; 310 | 311 | context_resource *ctx_res_p = NULL; 312 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 313 | return enif_raise_exception(env, 314 | make_response(env, "badarg","bad_context") 315 | ); 316 | } 317 | 318 | signal_protocol_address sender_address; 319 | const char * ret_val = create_signal_protocol_address(env, argv[1], &sender_address); 320 | if(ret_val != NULL) { 321 | return enif_raise_exception(env, 322 | make_response(env, "badarg", ret_val) 323 | ); 324 | } 325 | /* 326 | session_builder_resource *builder_res_p = NULL; 327 | if (!enif_get_resource(env, argv[1], SESSION_BUILDER_RESOURCE, (void**)&builder_res_p)) { 328 | return enif_raise_exception(env, 329 | make_response(env, "badarg","bad_session_builder") 330 | ); 331 | } 332 | */ 333 | ErlNifBinary handshake_bin; 334 | if (!enif_inspect_binary(env, argv[2], &handshake_bin)) { 335 | return enif_raise_exception(env, 336 | make_response(env, "badarg", "handshake") 337 | ); 338 | } 339 | 340 | esc_buf *buf = esc_buf_create(handshake_bin.data, handshake_bin.size); 341 | session_cipher *cipher = NULL; 342 | esc_address *address_from_p = NULL; 343 | 344 | err_msg = esc_handshake_acknowledge(buf, &sender_address, ctx_res_p->ctx_p, &cipher, &address_from_p); 345 | esc_buf_free(buf); 346 | if (err_msg!=NULL) { 347 | return make_response(env, "error", err_msg); 348 | } 349 | 350 | session_cipher_resource * cipher_res_p = (session_cipher_resource*) enif_alloc_resource(SESSION_CIPHER_RESOURCE, sizeof(session_cipher_resource)); 351 | cipher_res_p->session_cipher_p = cipher; 352 | ERL_NIF_TERM cipher_term = enif_make_resource(env, cipher_res_p); 353 | 354 | ERL_NIF_TERM address_from_term = construct_es_address_record(env, address_from_p); 355 | 356 | return enif_make_tuple2(env, 357 | enif_make_atom(env, "ok"), 358 | address_from_term 359 | ); 360 | } 361 | 362 | ERL_NIF_TERM nif_esc_encode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 363 | 364 | context_resource *ctx_res_p = NULL; 365 | const char * err_msg; 366 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 367 | return enif_make_badarg(env); 368 | } 369 | 370 | signal_protocol_address address; 371 | const char * ret_val = create_signal_protocol_address(env, argv[1], &address); 372 | if(ret_val != NULL) { 373 | return enif_raise_exception(env, 374 | make_response(env, "error", ret_val) 375 | ); 376 | } 377 | 378 | ErlNifBinary buffer; 379 | if (!enif_inspect_binary(env, argv[2], &buffer)) { 380 | return enif_make_badarg(env); 381 | } 382 | 383 | esc_buf *msg_p = signal_buffer_create(buffer.data, buffer.size); 384 | esc_buf *msg_encripted = NULL; 385 | 386 | err_msg = esc_message_encrypt_and_serialize(msg_p, &address, ctx_res_p->ctx_p, &msg_encripted); 387 | esc_buf_free(msg_p); 388 | 389 | if (err_msg != NULL ) { 390 | return make_response(env, "error", err_msg); 391 | } 392 | 393 | ERL_NIF_TERM result_bin; 394 | ErlNifBinary binary; 395 | enif_alloc_binary(esc_buf_get_len(msg_encripted), &binary); 396 | memcpy((char *) binary.data, (char *) esc_buf_get_data(msg_encripted), esc_buf_get_len(msg_encripted)); 397 | 398 | result_bin = enif_make_binary(env, &binary); 399 | 400 | ERL_NIF_TERM result = enif_make_tuple2(env, 401 | enif_make_atom(env, "ok"), 402 | result_bin 403 | ); 404 | 405 | esc_buf_free(msg_encripted); 406 | 407 | return result; 408 | }; 409 | 410 | ERL_NIF_TERM nif_esc_decode(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 411 | context_resource *ctx_res_p = NULL; 412 | const char * err_msg = NULL; 413 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 414 | return enif_make_badarg(env); 415 | } 416 | 417 | signal_protocol_address address; 418 | const char * ret_val = create_signal_protocol_address(env, argv[1], &address); 419 | if(ret_val != NULL) { 420 | return enif_raise_exception(env, 421 | make_response(env, "error", ret_val) 422 | ); 423 | } 424 | 425 | ErlNifBinary buffer; 426 | if (!enif_inspect_binary(env, argv[2], &buffer)) { 427 | return enif_make_badarg(env); 428 | } 429 | 430 | esc_buf *msg_p = signal_buffer_create(buffer.data, buffer.size); 431 | esc_buf *msg_decripted = NULL; 432 | 433 | es_log_hex("decoded: ", (char *) buffer.data, buffer.size); 434 | 435 | err_msg = esc_message_decrypt_from_serialized(msg_p, &address, ctx_res_p->ctx_p, &msg_decripted); 436 | if (err_msg) { 437 | return make_response(env, "error", err_msg); 438 | } 439 | 440 | ERL_NIF_TERM result_bin; 441 | ErlNifBinary binary; 442 | enif_alloc_binary(esc_buf_get_len(msg_decripted), &binary); 443 | memcpy((char *) binary.data, (char *) esc_buf_get_data(msg_decripted), esc_buf_get_len(msg_decripted)); 444 | result_bin = enif_make_binary(env, &binary); 445 | 446 | ERL_NIF_TERM result = enif_make_tuple2(env, 447 | enif_make_atom(env, "ok"), 448 | result_bin 449 | ); 450 | 451 | esc_buf_free(msg_decripted); 452 | 453 | return result; 454 | }; 455 | 456 | ERL_NIF_TERM nif_esc_serialize(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 457 | context_resource *ctx_res_p = NULL; 458 | const char * err_msg; 459 | if (!enif_get_resource(env, argv[0], CONTEXT_RESOURCE, (void**)&ctx_res_p)) { 460 | return enif_make_badarg(env); 461 | } 462 | esc_context *ctx_p = ctx_res_p->ctx_p; 463 | 464 | ERL_NIF_TERM sessions = ctx_p->session_store->serialize(env); 465 | ERL_NIF_TERM pre_keys = ctx_p->pre_key_store->serialize(env); 466 | ERL_NIF_TERM signed_pre_keys = ctx_p->signed_pre_key_store->serialize(env); 467 | ERL_NIF_TERM identity_keys = ctx_p->identity_key_store->serialize(env); 468 | ERL_NIF_TERM settings = ctx_p->settings->serialize(env); 469 | 470 | return enif_make_list5(env, 471 | enif_make_tuple2(env, enif_make_atom(env, "sessions"), sessions), 472 | //enif_make_tuple2(env, enif_make_atom(env, "pre_keys"), pre_keys), 473 | enif_make_tuple2(env, enif_make_atom(env, "pre_keys"), enif_make_atom(env, "internal")), 474 | enif_make_tuple2(env, enif_make_atom(env, "signed_pre_keys"), signed_pre_keys), 475 | enif_make_tuple2(env, enif_make_atom(env, "identity_keys"), identity_keys), 476 | enif_make_tuple2(env, enif_make_atom(env, "settings"), settings) 477 | ); 478 | } 479 | 480 | void esc_context_resource_destroy(ErlNifEnv* env, void* arg) { 481 | context_resource *ctx_res_p = (context_resource *) arg; 482 | esc_context_destroy_all(ctx_res_p->ctx_p); 483 | } 484 | 485 | void esc_session_builder_resource_destroy(ErlNifEnv* env, void* arg) { 486 | session_builder_resource * r = (session_builder_resource *) arg; 487 | session_builder_free(r->session_builder_p); 488 | } 489 | 490 | void esc_session_cipher_resource_destroy(ErlNifEnv* env, void* arg) { 491 | session_cipher_resource *r = ( session_cipher_resource * ) arg; 492 | session_cipher_free(r->session_cipher_p); 493 | } 494 | 495 | extern "C" { 496 | 497 | int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { 498 | 499 | ErlNifResourceFlags flags = (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); 500 | CONTEXT_RESOURCE = NULL; 501 | CONTEXT_RESOURCE = enif_open_resource_type(env, NULL, "esc_context_resource", &esc_context_resource_destroy, flags, NULL); 502 | if (CONTEXT_RESOURCE == NULL) { 503 | return -1; 504 | } 505 | 506 | SESSION_BUILDER_RESOURCE = NULL; 507 | SESSION_BUILDER_RESOURCE = enif_open_resource_type(env, NULL, "esc_session_builder_resource", &esc_session_builder_resource_destroy, flags, NULL); 508 | if (SESSION_BUILDER_RESOURCE == NULL) { 509 | return -2; 510 | } 511 | 512 | SESSION_CIPHER_RESOURCE = NULL; 513 | SESSION_CIPHER_RESOURCE = enif_open_resource_type(env, NULL, "esc_session_cipher_resource", &esc_session_cipher_resource_destroy, flags, NULL); 514 | if (SESSION_BUILDER_RESOURCE == NULL) { 515 | return -3; 516 | } 517 | return 0; 518 | } 519 | 520 | int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); 521 | 522 | static ErlNifFunc nif_funcs[] = { 523 | {"new", 0, nif_esc_new}, 524 | {"generate_identity_keys", 1, nif_esc_generate_identity_keys}, 525 | {"is_session_exists_initiated", 2, nif_esc_is_session_exists_initiated }, 526 | {"handshake_initiate", 3, nif_esc_handshake_initiate }, 527 | {"handshake_accept", 3, nif_esc_handshake_accept }, 528 | {"handshake_acknowledge", 3, nif_esc_handshake_acknowledge }, 529 | {"encode", 3, nif_esc_encode }, 530 | {"decode", 3, nif_esc_decode }, 531 | {"serialize", 1, nif_esc_serialize} 532 | }; 533 | 534 | ERL_NIF_INIT(erl_signal_nif, nif_funcs, &on_load, NULL, NULL, NULL); 535 | }; -------------------------------------------------------------------------------- /c_src/erl_signal_store.cpp: -------------------------------------------------------------------------------- 1 | #include // int types 2 | #include // printf 3 | #include // exit 4 | #include // strlen 5 | 6 | #include 7 | #include 8 | 9 | #include "libsignal-c/signal_protocol.h" 10 | #include "libsignal-c/key_helper.h" 11 | 12 | // #include 13 | 14 | #include "erl_signal_client.h" 15 | #include "erl_signal_store.h" 16 | #include "erl_signal_log.h" 17 | 18 | #define INIT_STATUS_NAME "init_status" 19 | #define OWN_PUBLIC_KEY_NAME "own_public_key" 20 | #define OWN_PRIVATE_KEY_NAME "own_private_key" 21 | #define OWN_KEY 2 22 | #define REG_ID_NAME "axolotl_registration_id" 23 | #define IDENTITY_KEY_TRUSTED 1 24 | #define IDENTITY_KEY_UNTRUSTED 1 25 | 26 | #define SESSION_STORE_TABLE_NAME "session_store" 27 | #define SESSION_STORE_NAME_NAME "name" 28 | #define SESSION_STORE_NAME_LEN_NAME "name_len" 29 | #define SESSION_STORE_DEVICE_ID_NAME "device_id" 30 | #define SESSION_STORE_RECORD_NAME "session_record" 31 | #define SESSION_STORE_RECORD_LEN_NAME "record_len" 32 | #define PRE_KEY_STORE_TABLE_NAME "pre_key_store" 33 | #define PRE_KEY_STORE_ID_NAME "id" 34 | #define PRE_KEY_STORE_RECORD_NAME "pre_key_record" 35 | #define PRE_KEY_STORE_RECORD_LEN_NAME "record_len" 36 | #define SIGNED_PRE_KEY_STORE_TABLE_NAME "signed_pre_key_store" 37 | #define SIGNED_PRE_KEY_STORE_ID_NAME "id" 38 | #define SIGNED_PRE_KEY_STORE_RECORD_NAME "signed_pre_key_record" 39 | #define SIGNED_PRE_KEY_STORE_RECORD_LEN_NAME "record_len" 40 | #define IDENTITY_KEY_STORE_TABLE_NAME "identity_key_store" 41 | #define IDENTITY_KEY_STORE_NAME_NAME "name" 42 | #define IDENTITY_KEY_STORE_KEY_NAME "key" 43 | #define IDENTITY_KEY_STORE_KEY_LEN_NAME "key_len" 44 | #define IDENTITY_KEY_STORE_TRUSTED_NAME "trusted" 45 | #define SETTINGS_STORE_TABLE_NAME "settings" 46 | #define SETTINGS_STORE_NAME_NAME "name" 47 | #define SETTINGS_STORE_PROPERTY_NAME "property" 48 | 49 | int esc_db_property_set(const char * name, const int val, esc_context * esc_ctx_p) { 50 | esc_storage::value str = std::to_string(val); 51 | esc_storage::row row; 52 | row.store(esc_storage::column("p"), str); 53 | esc_ctx_p->settings->set(name, row); 54 | return 0; 55 | } 56 | 57 | int esc_db_property_get(const char * name, int * val_p, esc_context * esc_ctx_p) { 58 | const esc_storage::row row = esc_ctx_p->settings->get(std::string(name)); 59 | esc_storage::value value = row.get(esc_storage::column("p"), "0"); 60 | *val_p = std::stoi(value); 61 | return 0; 62 | } 63 | 64 | int esc_db_init_status_set(const int status, esc_context * esc_ctx_p) { 65 | return esc_db_property_set(INIT_STATUS_NAME, status, esc_ctx_p); 66 | } 67 | 68 | int esc_db_init_status_get(int * init_status_p, esc_context * esc_ctx_p) { 69 | return esc_db_property_get(INIT_STATUS_NAME, init_status_p, esc_ctx_p); 70 | } 71 | 72 | std::string broadcast_address_string(const signal_protocol_address *address) { 73 | return std::string(address->name, address->name_len); 74 | } 75 | 76 | std::string address_string(const signal_protocol_address *address) { 77 | std::string str = broadcast_address_string(address); 78 | str.append("_").append(std::to_string(address->device_id)); 79 | return str; 80 | } 81 | 82 | 83 | // session store impl 84 | int esc_db_session_load(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data) { 85 | std::string key = address_string(address); 86 | esc_context * esc_ctx_p = (esc_context *) user_data; 87 | esc_storage::row row = esc_ctx_p->session_store->get(key); 88 | 89 | if (row.is_empty()) { 90 | *record = NULL; 91 | *user_record = NULL; 92 | return 0; 93 | } 94 | 95 | std::string session_record = row.get("session_record",""); 96 | int session_record_len = std::stoi(row.get("session_record_len", "0")); 97 | *record = signal_buffer_create((const uint8_t *) session_record.c_str(), session_record_len); 98 | 99 | // es_log_hex("load session record: ", (char *) session_record.c_str(), session_record_len); 100 | 101 | std::string user_record_str = row.get("user_record",""); 102 | int user_record_len = std::stoi(row.get("user_record_len", "0")); 103 | *user_record = signal_buffer_create((const uint8_t *) user_record_str.c_str(), user_record_len); 104 | 105 | return 1; 106 | } 107 | 108 | int esc_db_session_get_sub_device_sessions(signal_int_list ** sessions, const char * name, size_t name_len, void * user_data) { 109 | 110 | std::string name_str = std::string(name, name_len); 111 | esc_context * esc_ctx_p = (esc_context *) user_data; 112 | 113 | std::list result = esc_ctx_p->session_store->get_nearby(name_str); 114 | 115 | signal_int_list * session_list_p = signal_int_list_alloc();; 116 | 117 | for(std::list ::const_iterator iterator = result.begin(); iterator != result.end(); iterator++) { 118 | esc_storage::row row = *iterator; 119 | std::string device_id_str = row.get("device_id", ""); 120 | if (device_id_str.size() > 0) { 121 | signal_int_list_push_back(session_list_p, std::stoi(device_id_str)); 122 | } 123 | } 124 | 125 | *sessions = session_list_p; 126 | 127 | return signal_int_list_size(session_list_p); 128 | } 129 | 130 | int esc_db_session_store(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data) { 131 | 132 | /* 133 | es_log("save session"); 134 | es_log(address_string(address).c_str()); 135 | es_log_hex("session record: ", (char *) record, record_len); 136 | */ 137 | 138 | esc_storage::row row; 139 | row.store(esc_storage::column("name"), esc_storage::value(address->name)); 140 | row.store(esc_storage::column("name_len"), esc_storage::value(std::to_string(address->name_len))); 141 | 142 | row.store(esc_storage::column("device_id"), esc_storage::value(std::to_string(address->device_id))); 143 | 144 | row.store(esc_storage::column("session_record"), esc_storage::value(std::string((char *) record, record_len))); 145 | row.store(esc_storage::column("session_record_len"), esc_storage::value(std::to_string(record_len))); 146 | 147 | row.store(esc_storage::column("user_record"), esc_storage::value(std::string((char *) user_record, user_record_len))); 148 | row.store(esc_storage::column("user_record_len"), esc_storage::value(std::to_string(user_record_len))); 149 | 150 | esc_context * esc_ctx_p = (esc_context *) user_data; 151 | 152 | esc_ctx_p->session_store->set(address_string(address), row); 153 | 154 | return 0; 155 | } 156 | 157 | int esc_db_session_contains(const signal_protocol_address * address, void * user_data) { 158 | 159 | std::string key = address_string(address); 160 | esc_context * esc_ctx_p = (esc_context *) user_data; 161 | esc_storage::row row = esc_ctx_p->session_store->get(key); 162 | 163 | es_log(key.c_str()); 164 | 165 | if (row.is_empty()) { 166 | es_log("session not found"); 167 | return 0; 168 | } else { 169 | es_log("session found"); 170 | return 1; 171 | } 172 | } 173 | 174 | int esc_db_session_delete(const signal_protocol_address * address, void * user_data) { 175 | 176 | std::string key = address_string(address); 177 | esc_context * esc_ctx_p = (esc_context *) user_data; 178 | esc_storage::row row = esc_ctx_p->session_store->get(key); 179 | esc_ctx_p->session_store->erase(key); 180 | if (row.is_empty()) { 181 | return 0; 182 | } else { 183 | return 1; 184 | } 185 | } 186 | 187 | int esc_db_session_delete_all(const char * name, size_t name_len, void * user_data) { 188 | 189 | std::string name_str = std::string(name, name_len); 190 | esc_context * esc_ctx_p = (esc_context *) user_data; 191 | 192 | return esc_ctx_p->session_store->erase_nearby(name_str); 193 | } 194 | 195 | void esc_db_session_destroy_store_ctx(void * user_data) { 196 | esc_context * esc_ctx_p = (esc_context *) user_data; 197 | esc_ctx_p->session_store->clear(); 198 | } 199 | 200 | // pre key store impl 201 | int esc_db_pre_key_load(signal_buffer ** record, uint32_t pre_key_id, void * user_data) { 202 | esc_context * esc_ctx_p = (esc_context *) user_data; 203 | 204 | std::string key = std::to_string(pre_key_id); 205 | 206 | esc_storage::row row = esc_ctx_p->pre_key_store->get(key); 207 | 208 | if(row.is_empty()) { 209 | return SG_ERR_INVALID_KEY_ID; 210 | } 211 | 212 | std::string pre_key = row.get("pre_key",""); 213 | int pre_key_len = std::stoi(row.get("pre_key_len", "0")); 214 | *record = signal_buffer_create((const uint8_t *) pre_key.c_str(), pre_key_len); 215 | 216 | return SG_SUCCESS; 217 | } 218 | 219 | int esc_db_pre_key_store(uint32_t pre_key_id, uint8_t * record, size_t record_len, void * user_data) { 220 | esc_context * esc_ctx_p = (esc_context *) user_data; 221 | 222 | esc_storage::row row; 223 | row.store(esc_storage::column("pre_key"), std::string((char *) record, record_len)); 224 | row.store(esc_storage::column("pre_key_len"), esc_storage::value(std::to_string(record_len))); 225 | 226 | std::string key = std::to_string(pre_key_id); 227 | 228 | esc_ctx_p->pre_key_store->set(key, row); 229 | 230 | return 0; 231 | } 232 | 233 | int esc_db_pre_key_store_list(signal_protocol_key_helper_pre_key_list_node * pre_keys_head, void * user_data) { 234 | esc_context * esc_ctx_p = (esc_context *) user_data; 235 | 236 | signal_protocol_key_helper_pre_key_list_node * pre_keys_curr_p = NULL; 237 | signal_buffer * key_buf_p = NULL; 238 | session_pre_key * pre_key_p = NULL; 239 | 240 | pre_keys_curr_p = pre_keys_head; 241 | struct data { 242 | uint32_t id; 243 | std::string pre_key; 244 | }; 245 | std::list result; 246 | while (pre_keys_curr_p) { 247 | pre_key_p = signal_protocol_key_helper_key_list_element(pre_keys_curr_p); 248 | if (session_pre_key_serialize(&key_buf_p, pre_key_p)) { 249 | return -1; 250 | } 251 | 252 | uint32_t pre_key_id = session_pre_key_get_id(pre_key_p); 253 | std::string pre_key = std::string((char *) signal_buffer_data(key_buf_p), signal_buffer_len(key_buf_p)); 254 | data d = { 255 | .id = pre_key_id, 256 | .pre_key = pre_key 257 | }; 258 | result.push_back(d); 259 | pre_keys_curr_p = signal_protocol_key_helper_key_list_next(pre_keys_curr_p); 260 | } 261 | 262 | for(std::list ::const_iterator iterator = result.begin(); iterator != result.end(); iterator++) { 263 | data d = *iterator; 264 | 265 | esc_storage::row row; 266 | row.store(esc_storage::column("pre_key"), d.pre_key); 267 | row.store(esc_storage::column("pre_key_len"), std::to_string(d.pre_key.length())); 268 | 269 | std::string key = std::to_string(d.id); 270 | 271 | esc_ctx_p->pre_key_store->set(key, row); 272 | } 273 | 274 | return 0; 275 | } 276 | 277 | int esc_db_pre_key_get_list(size_t amount, void * user_data, esc_buf_list_item ** list_head_pp) { 278 | esc_context * esc_ctx_p = (esc_context *) user_data; 279 | const char * err_msg; 280 | 281 | esc_buf_list_item *list = NULL; 282 | esc_buf_list_item *head = NULL; 283 | ec_key_pair * pre_key_pair_p = NULL; 284 | ec_public_key * pre_key_public_p = NULL; 285 | esc_buf * pre_key_public_serialized_p = NULL; 286 | esc_buf_list_item * temp_item_p = NULL; 287 | 288 | int ret_val = 0; 289 | 290 | session_pre_key * pre_key_p = NULL; 291 | 292 | for(esc_storage::storage::const_iterator it = esc_ctx_p->pre_key_store->begin(); it!=esc_ctx_p->pre_key_store->end(); it++) { 293 | uint32_t key_id = std::stoi(it->first); 294 | esc_storage::row row = it->second; 295 | 296 | std::string pre_key_str_serialized = row.get("pre_key", ""); 297 | 298 | ret_val = session_pre_key_deserialize(&pre_key_p, (uint8_t *) pre_key_str_serialized.c_str(), pre_key_str_serialized.size(), esc_ctx_p->global_context_p); 299 | if (ret_val) { 300 | err_msg = "failed to deserialize pre_key"; 301 | goto cleanup; 302 | } 303 | 304 | pre_key_pair_p = session_pre_key_get_key_pair(pre_key_p); 305 | pre_key_public_p = ec_key_pair_get_public(pre_key_pair_p); 306 | 307 | ret_val = ec_public_key_serialize(&pre_key_public_serialized_p, pre_key_public_p); 308 | if (ret_val) { 309 | err_msg = "failed to serialize public key"; 310 | goto cleanup; 311 | } 312 | 313 | ret_val = esc_buf_list_item_create(&temp_item_p, &key_id, pre_key_public_serialized_p); 314 | if (ret_val) { 315 | err_msg = "failed to create list item"; 316 | goto cleanup; 317 | } 318 | 319 | //esc_buf_free(serialized_keypair_data_p); 320 | 321 | SIGNAL_UNREF(pre_key_p); 322 | pre_key_p = NULL; 323 | 324 | esc_buf_list_item_create(&head, NULL, NULL); 325 | head->next_p = list; 326 | list = head; 327 | } 328 | 329 | /** 330 | const char stmt[] = "SELECT * FROM " PRE_KEY_STORE_TABLE_NAME 331 | " ORDER BY " PRE_KEY_STORE_ID_NAME " ASC LIMIT ?1;"; 332 | 333 | int ret_val = -1; 334 | char * err_msg = NULL; 335 | 336 | sqlite3 * db_p = NULL; 337 | sqlite3_stmt * pstmt_p = NULL; 338 | esc_buf_list_item * head_p = NULL; 339 | esc_buf_list_item * curr_p = NULL; 340 | uint32_t key_id = 0; 341 | esc_buf * serialized_keypair_data_p = NULL; 342 | size_t record_len = 0; 343 | 344 | ec_key_pair * pre_key_pair_p = NULL; 345 | ec_public_key * pre_key_public_p = NULL; 346 | esc_buf * pre_key_public_serialized_p = NULL; 347 | esc_buf_list_item * temp_item_p = NULL; 348 | 349 | if (db_conn_open(&db_p, &pstmt_p, stmt, esc_ctx_p)) return -1; 350 | 351 | ret_val = sqlite3_bind_int(pstmt_p, 1, amount); 352 | if (ret_val) { 353 | err_msg = "failed to bind"; 354 | goto cleanup; 355 | } 356 | 357 | ret_val = esc_buf_list_item_create(&head_p, NULL, NULL); 358 | if (ret_val) { 359 | err_msg = "failed to create list"; 360 | goto cleanup; 361 | } 362 | 363 | curr_p = head_p; 364 | ret_val = sqlite3_step(pstmt_p); 365 | while (ret_val == SQLITE_ROW) { 366 | key_id = sqlite3_column_int(pstmt_p, 0); 367 | record_len = sqlite3_column_int(pstmt_p, 2); 368 | 369 | serialized_keypair_data_p = signal_buffer_create(sqlite3_column_blob(pstmt_p, 1), record_len); 370 | if (!serialized_keypair_data_p) { 371 | err_msg = "failed to initialize buffer"; 372 | ret_val = -3; 373 | goto cleanup; 374 | } 375 | 376 | ret_val = session_pre_key_deserialize(&pre_key_p, esc_buf_get_data(serialized_keypair_data_p), record_len, esc_context_get_axolotl_ctx(esc_ctx_p)); 377 | if (ret_val) { 378 | err_msg = "failed to deserialize keypair"; 379 | goto cleanup; 380 | } 381 | 382 | pre_key_pair_p = session_pre_key_get_key_pair(pre_key_p); 383 | pre_key_public_p = ec_key_pair_get_public(pre_key_pair_p); 384 | 385 | ret_val = ec_public_key_serialize(&pre_key_public_serialized_p, pre_key_public_p); 386 | if (ret_val) { 387 | err_msg = "failed to serialize public key"; 388 | goto cleanup; 389 | } 390 | 391 | ret_val = esc_buf_list_item_create(&temp_item_p, &key_id, pre_key_public_serialized_p); 392 | if (ret_val) { 393 | err_msg = "failed to create list item"; 394 | goto cleanup; 395 | } 396 | 397 | esc_buf_list_item_set_next(curr_p, temp_item_p); 398 | curr_p = esc_buf_list_item_get_next(curr_p); 399 | 400 | esc_buf_free(serialized_keypair_data_p); 401 | 402 | SIGNAL_UNREF(pre_key_p); 403 | pre_key_p = NULL; 404 | ret_val = sqlite3_step(pstmt_p); 405 | } 406 | 407 | if (ret_val != SQLITE_DONE) { 408 | err_msg = "sql error when retrieving keys"; 409 | goto cleanup; 410 | } 411 | 412 | *list_head_pp = esc_buf_list_item_get_next(head_p); 413 | ret_val = 0; 414 | */ 415 | cleanup: 416 | if (ret_val) { 417 | //esc_buf_free(serialized_keypair_data_p); 418 | SIGNAL_UNREF(pre_key_p); 419 | esc_buf_free(pre_key_public_serialized_p); 420 | esc_buf_list_free(list); 421 | return ret_val; 422 | } else { 423 | *list_head_pp = list; 424 | (void *) err_msg; 425 | return 0; 426 | } 427 | 428 | 429 | } 430 | 431 | int esc_db_pre_key_contains(uint32_t pre_key_id, void * user_data) { 432 | esc_context * esc_ctx_p = (esc_context *) user_data; 433 | 434 | std::string key = std::to_string(pre_key_id); 435 | if(esc_ctx_p->pre_key_store->is_member(key)) { 436 | return 1; 437 | } else { 438 | return 0; 439 | } 440 | } 441 | 442 | int esc_db_pre_key_get_max_id(void * user_data, uint32_t * max_id_p) { 443 | esc_context * esc_ctx_p = (esc_context *) user_data; 444 | 445 | std::list keys = esc_ctx_p->pre_key_store->keys(); 446 | 447 | std::list ids; 448 | 449 | for(std::list ::const_iterator it = keys.begin(); it != keys.end(); it++) { 450 | ids.push_front((uint32_t) std::stoi(*it)); 451 | } 452 | 453 | *max_id_p = *std::max_element(ids.begin(), ids.end()); 454 | return 0; 455 | } 456 | 457 | int esc_db_pre_key_get_count(void * user_data, size_t * count_p) { 458 | esc_context * esc_ctx_p = (esc_context *) user_data; 459 | 460 | std::list keys = esc_ctx_p->pre_key_store->keys(); 461 | 462 | *count_p = esc_ctx_p->pre_key_store->size(); 463 | return 0; 464 | } 465 | 466 | int esc_db_pre_key_remove(uint32_t pre_key_id, void * user_data) { 467 | esc_context * esc_ctx_p = (esc_context *) user_data; 468 | 469 | std::string key = std::to_string(pre_key_id); 470 | 471 | bool is_member = esc_ctx_p->pre_key_store->is_member(key); 472 | esc_ctx_p->pre_key_store->erase(key); 473 | if (is_member) { 474 | return 0; 475 | } else { 476 | return -4; 477 | } 478 | } 479 | 480 | void esc_db_pre_key_destroy_ctx(void * user_data) { 481 | esc_context * esc_ctx_p = (esc_context *) user_data; 482 | 483 | esc_ctx_p->pre_key_store->clear(); 484 | } 485 | 486 | // signed pre key store impl 487 | int esc_db_signed_pre_key_load(signal_buffer ** record, uint32_t signed_pre_key_id, void * user_data) { 488 | esc_context * esc_ctx_p = (esc_context *) user_data; 489 | 490 | std::string key = std::to_string(signed_pre_key_id); 491 | 492 | esc_storage::row row = esc_ctx_p->signed_pre_key_store->get(key); 493 | 494 | if(row.is_empty()) { 495 | return SG_ERR_INVALID_KEY_ID; 496 | } 497 | 498 | std::string signed_pre_key = row.get("signed_pre_key",""); 499 | int pre_key_len = std::stoi(row.get("signed_pre_key_len", "0")); 500 | *record = signal_buffer_create((const uint8_t *) signed_pre_key.c_str(), pre_key_len); 501 | 502 | return SG_SUCCESS; 503 | } 504 | 505 | int esc_db_signed_pre_key_store(uint32_t signed_pre_key_id, uint8_t * record, size_t record_len, void * user_data) { 506 | esc_context * esc_ctx_p = (esc_context *) user_data; 507 | 508 | esc_storage::row row; 509 | row.store(esc_storage::column("signed_pre_key"), std::string((char *) record, record_len)); 510 | row.store(esc_storage::column("signed_pre_key_len"), esc_storage::value(std::to_string(record_len))); 511 | 512 | std::string key = std::to_string(signed_pre_key_id); 513 | 514 | esc_ctx_p->signed_pre_key_store->set(key, row); 515 | 516 | return 0; 517 | } 518 | 519 | int esc_db_signed_pre_key_contains(uint32_t signed_pre_key_id, void * user_data) { 520 | esc_context * esc_ctx_p = (esc_context *) user_data; 521 | 522 | std::string key = std::to_string(signed_pre_key_id); 523 | if(esc_ctx_p->signed_pre_key_store->is_member(key)) { 524 | return 1; 525 | } else { 526 | return 0; 527 | } 528 | } 529 | 530 | int esc_db_signed_pre_key_remove(uint32_t signed_pre_key_id, void * user_data) { 531 | esc_context * esc_ctx_p = (esc_context *) user_data; 532 | 533 | std::string key = std::to_string(signed_pre_key_id); 534 | 535 | bool is_member = esc_ctx_p->signed_pre_key_store->is_member(key); 536 | esc_ctx_p->pre_key_store->erase(key); 537 | if (is_member) { 538 | return 0; 539 | } else { 540 | return -4; 541 | } 542 | } 543 | 544 | void esc_db_signed_pre_key_destroy_ctx(void * user_data) { 545 | esc_context * esc_ctx_p = (esc_context *) user_data; 546 | 547 | esc_ctx_p->signed_pre_key_store->clear(); 548 | 549 | } 550 | 551 | // identity key store impl 552 | /** 553 | * saves the public and private key by using the api serialization calls, as this format (and not the higher-level key type) is needed by the getter. 554 | */ 555 | int esc_db_identity_set_key_pair(const ratchet_identity_key_pair * key_pair_p, void * user_data) { 556 | // 1 - name ("public" or "private") 557 | // 2 - key blob 558 | // 3 - length of the key 559 | // 4 - trusted (1 for true, 0 for false) 560 | 561 | esc_context * esc_ctx_p = (esc_context *) user_data; 562 | int ret_val = 0; 563 | const char * err_msg = NULL; 564 | 565 | esc_storage::row row_public; 566 | signal_buffer * pubkey_buf_p = NULL; 567 | size_t pubkey_buf_len = 0; 568 | uint8_t * pubkey_buf_data_p = NULL; 569 | 570 | esc_storage::row row_private; 571 | signal_buffer * privkey_buf_p = NULL; 572 | size_t privkey_buf_len = 0; 573 | uint8_t * privkey_buf_data_p = NULL; 574 | 575 | if (ec_public_key_serialize(&pubkey_buf_p, ratchet_identity_key_pair_get_public(key_pair_p))) { 576 | err_msg = "Failed to allocate memory to serialize the public key"; 577 | ret_val = SG_ERR_NOMEM; 578 | goto cleanup; 579 | } 580 | 581 | pubkey_buf_len = signal_buffer_len(pubkey_buf_p); 582 | pubkey_buf_data_p = signal_buffer_data(pubkey_buf_p); 583 | 584 | row_public.store("name", OWN_PUBLIC_KEY_NAME); 585 | row_public.store("blob", std::string((char *) pubkey_buf_data_p, pubkey_buf_len)); 586 | row_public.store("blob_len", std::to_string(pubkey_buf_len)); 587 | row_public.store("trusted", std::to_string(OWN_KEY)); 588 | 589 | esc_ctx_p->identity_key_store->set(OWN_PUBLIC_KEY_NAME, row_public); 590 | 591 | if (ec_private_key_serialize(&privkey_buf_p, ratchet_identity_key_pair_get_private(key_pair_p))) { 592 | err_msg = "Failed to allocate memory to serialize the private key"; 593 | ret_val = SG_ERR_NOMEM; 594 | goto cleanup; 595 | } 596 | 597 | privkey_buf_len = signal_buffer_len(privkey_buf_p); 598 | privkey_buf_data_p = signal_buffer_data(privkey_buf_p); 599 | 600 | 601 | // es_log_hex("save public_blob ", (char *) pubkey_buf_data_p, pubkey_buf_len); 602 | // es_log_hex("save private_blob ", (char *) privkey_buf_data_p, privkey_buf_len); 603 | 604 | row_private.store("name", OWN_PRIVATE_KEY_NAME); 605 | row_private.store("blob", std::string((char *) privkey_buf_data_p, privkey_buf_len)); 606 | row_private.store("blob_len", std::to_string(privkey_buf_len)); 607 | row_private.store("trusted", std::to_string(OWN_KEY)); 608 | 609 | esc_ctx_p->identity_key_store->set(OWN_PRIVATE_KEY_NAME, row_private); 610 | 611 | cleanup: 612 | if (pubkey_buf_p) { 613 | signal_buffer_bzero_free(pubkey_buf_p); 614 | } 615 | if (privkey_buf_p) { 616 | signal_buffer_bzero_free(privkey_buf_p); 617 | } 618 | if (!err_msg) { 619 | es_log(err_msg); 620 | } 621 | 622 | return ret_val; 623 | } 624 | 625 | int esc_db_identity_get_key_pair(signal_buffer ** public_data, signal_buffer ** private_data, void * user_data) { 626 | 627 | esc_context * esc_ctx_p = (esc_context *) user_data; 628 | int ret_val = 0; 629 | const char * err_msg = NULL; 630 | 631 | esc_storage::row row_public = esc_ctx_p->identity_key_store->get(OWN_PUBLIC_KEY_NAME); 632 | signal_buffer * pubkey_buf_p = NULL; 633 | size_t pubkey_buf_len = 0; 634 | 635 | esc_storage::row row_private; 636 | std::string private_blob, public_blob; 637 | signal_buffer * privkey_buf_p = NULL; 638 | size_t privkey_buf_len = 0; 639 | 640 | if (row_public.is_empty()) { 641 | err_msg = "Own public key not found"; 642 | ret_val = SG_ERR_INVALID_KEY_ID; 643 | goto cleanup; 644 | } 645 | 646 | public_blob = row_public.get("blob", ""); 647 | pubkey_buf_len = std::stoi(row_public.get("blob_len", "0")); 648 | 649 | pubkey_buf_p = signal_buffer_create((uint8_t *) public_blob.c_str(), pubkey_buf_len); 650 | 651 | 652 | row_private = esc_ctx_p->identity_key_store->get(OWN_PRIVATE_KEY_NAME); 653 | 654 | if (row_private.is_empty()) { 655 | err_msg = "Own private key not found"; 656 | ret_val = SG_ERR_INVALID_KEY_ID; 657 | goto cleanup; 658 | } 659 | 660 | private_blob = row_private.get("blob", ""); 661 | privkey_buf_len = std::stoi(row_private.get("blob_len", "0")); 662 | 663 | privkey_buf_p = signal_buffer_create((uint8_t *) private_blob.c_str(), privkey_buf_len); 664 | 665 | *public_data = pubkey_buf_p; 666 | *private_data = privkey_buf_p; 667 | 668 | cleanup: 669 | if (ret_val < 0) { 670 | if (pubkey_buf_p) { 671 | signal_buffer_bzero_free(pubkey_buf_p); 672 | } 673 | if (privkey_buf_p) { 674 | signal_buffer_bzero_free(privkey_buf_p); 675 | } 676 | es_log(err_msg); 677 | } else { 678 | //es_log_hex("load public_blob ",public_blob.c_str(), public_blob.size()); 679 | //es_log_hex("load private_blob ",private_blob.c_str(), private_blob.size()); 680 | } 681 | 682 | return ret_val; 683 | } 684 | 685 | int esc_db_identity_set_local_registration_id(const uint32_t reg_id, void * user_data) { 686 | esc_context * esc_ctx_p = (esc_context *) user_data; 687 | return (esc_db_property_set(REG_ID_NAME, reg_id, esc_ctx_p)) ? -1 : 0; 688 | } 689 | 690 | int esc_db_identity_get_local_registration_id(void * user_data, uint32_t * registration_id) { 691 | esc_context * esc_ctx_p = (esc_context *) user_data; 692 | return (esc_db_property_get(REG_ID_NAME, (int *) registration_id, esc_ctx_p ) != 0) ? -1 : 0; 693 | } 694 | 695 | int esc_db_identity_save(const signal_protocol_address * addr_p, uint8_t * key_data, size_t key_len, void * user_data) { 696 | esc_context * esc_ctx_p = (esc_context *) user_data; 697 | std::string address = broadcast_address_string(addr_p); 698 | if (key_data) { 699 | std::string blob = std::string((char *) key_data, key_len); 700 | 701 | esc_storage::row row; 702 | row.store("name", address); 703 | row.store("blob", blob); 704 | row.store("blob_len", std::to_string(blob.size())); 705 | row.store("trusted", std::to_string(IDENTITY_KEY_TRUSTED)); 706 | 707 | esc_ctx_p->identity_key_store->set(address, row); 708 | } else { 709 | esc_ctx_p->identity_key_store->erase(address); 710 | } 711 | return 0; 712 | } 713 | 714 | int esc_db_identity_is_trusted(const char * name, size_t name_len, uint8_t * key_data, size_t key_len, void * user_data) { 715 | esc_context * esc_ctx_p = (esc_context *) user_data; 716 | std::string name_str = std::string(name, name_len); 717 | 718 | esc_storage::row row = esc_ctx_p->identity_key_store->get(name_str); 719 | std::string trusted = row.get("trusted", ""); 720 | if (trusted == "") { 721 | return 1; 722 | } 723 | 724 | std::string key = std::string((char *) key_data, key_len); 725 | if (key == row.get("blob", "")) { 726 | return 1; 727 | } else { 728 | return 0; 729 | } 730 | } 731 | 732 | int esc_db_identity_always_trusted(const signal_protocol_address * addr_p, uint8_t * key_data, size_t key_len, void * user_data) { 733 | return 1; 734 | } 735 | 736 | void esc_db_identity_destroy_ctx(void * user_data) { 737 | esc_context * esc_ctx_p = (esc_context *) user_data; 738 | esc_ctx_p->identity_key_store->clear(); 739 | } 740 | -------------------------------------------------------------------------------- /c_src/erl_signal_store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "libsignal-c/signal_protocol.h" 4 | #include "libsignal-c/key_helper.h" 5 | 6 | #include "erl_signal_client.h" 7 | 8 | #define ESC_DB_NOT_INITIALIZED (-1) 9 | #define ESC_DB_NEEDS_ROLLBACK 0 10 | #define ESC_DB_INITIALIZED 1 11 | 12 | // session store 13 | int esc_db_session_load(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data); 14 | int esc_db_session_get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data); 15 | int esc_db_session_store(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data); 16 | int esc_db_session_contains(const signal_protocol_address *address, void *user_data); 17 | int esc_db_session_delete(const signal_protocol_address *address, void *user_data); 18 | int esc_db_session_delete_all(const char *name, size_t name_len, void *user_data); 19 | void esc_db_session_destroy_store_ctx(void *user_data); 20 | 21 | // pre key store 22 | int esc_db_pre_key_load(signal_buffer **record, uint32_t pre_key_id, void *user_data); 23 | int esc_db_pre_key_store(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); 24 | int esc_db_pre_key_contains(uint32_t pre_key_id, void *user_data); 25 | int esc_db_pre_key_remove(uint32_t pre_key_id, void *user_data); 26 | void esc_db_pre_key_destroy_ctx(void *user_data); 27 | /** 28 | * Stores a whole list of pre keys at once, inside a single transaction. 29 | * 30 | * @param pre_keys_head Pointer to the first element of the list. 31 | * @param user_data_p Optional. The user_data as received from the axolotl interface, will be used to set the database name. 32 | */ 33 | int esc_db_pre_key_store_list(signal_protocol_key_helper_pre_key_list_node * pre_keys_head, void * user_data); 34 | 35 | /** 36 | * Gets the specified number of pre keys for publishing, i.e. only their public part. 37 | * 38 | * @param amount Number of keys to retrieve. 39 | * @param ctx_p Pointer to the initialized axc context. 40 | * @param list_head_pp Will be set to the head of the list. 41 | * @return 0 on success, negative on error. 42 | */ 43 | int esc_db_pre_key_get_list(size_t amount, void * user_data, esc_buf_list_item ** list_head_pp); 44 | 45 | /** 46 | * Retrieves the highest existing pre key ID that is not the last resort key's ID. 47 | * 48 | * @param ctx_p Pointer to the axc context. 49 | * @param max_id_p Will be set to the highest ID that is not MAX_INT. 50 | * @return 0 on success, negative on error. 51 | */ 52 | int esc_db_pre_key_get_max_id(esc_context * ctx_p, uint32_t * max_id_p); 53 | 54 | /** 55 | * Returns the count of pre keys saved in the database. 56 | * This includes the "last resort" key that is additionally generated at db init. 57 | * 58 | * @param ctx_p Pointer to the axc context. 59 | * @param count_p Will point to the number of pre keys. 60 | * @return 0 on success, negative on error. 61 | */ 62 | int esc_db_pre_key_get_count(esc_context * ctx_p, size_t * count_p); 63 | 64 | // signed pre key store 65 | int esc_db_signed_pre_key_load(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data); 66 | int esc_db_signed_pre_key_store(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); 67 | int esc_db_signed_pre_key_contains(uint32_t signed_pre_key_id, void *user_data); 68 | int esc_db_signed_pre_key_remove(uint32_t signed_pre_key_id, void *user_data); 69 | void esc_db_signed_pre_key_destroy_ctx(void *user_data); 70 | 71 | // identity key store 72 | int esc_db_identity_get_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data); 73 | int esc_db_identity_get_local_registration_id(void *user_data, uint32_t *registration_id); 74 | int esc_db_identity_save(const signal_protocol_address * addr_p, uint8_t *key_data, size_t key_len, void *user_data); 75 | int esc_db_identity_is_trusted(const char *name, size_t name_len, uint8_t *key_data, size_t key_len, void *user_data); 76 | int esc_db_identity_always_trusted(const signal_protocol_address * addr_p, uint8_t * key_data, size_t key_len, void * user_data); 77 | void esc_db_identity_destroy_ctx(void *user_data); 78 | 79 | // additional helper functions 80 | /** 81 | * Saves the public and private key by using the api serialization calls, as this format (and not the higher-level key type) is needed by the getter. 82 | * 83 | * @param Pointer to the keypair as returned by axolotl_key_helper_generate_identity_key_pair 84 | * @param esc_ctx_p Pointer to the axc context. 85 | * @return 0 on success, negative on error 86 | */ 87 | int esc_db_identity_set_key_pair(const ratchet_identity_key_pair * key_pair_p, void * user_data); 88 | 89 | /** 90 | * Saves the axolotl registration ID which was obtained by a call to axolotl_key_helper_generate_registration_id(). 91 | * 92 | * @param reg_id The ID. 93 | * @param esc_ctx_p Pointer to the axc context. 94 | * @return 0 on success, negative on error 95 | */ 96 | int esc_db_identity_set_local_registration_id(const uint32_t reg_id, void * user_data); 97 | 98 | // other 99 | /** 100 | * Creates the necessary tables. Safe to call if they already exist. 101 | * 102 | * @param esc_ctx_p Pointer to the axc context. 103 | * @return 0 on success, negative on error 104 | */ 105 | int esc_db_create(esc_context * esc_ctx_p); 106 | 107 | /** 108 | * Drops all the tables so that the db can be reset. 109 | * 110 | * @param esc_ctx_p Pointer to the axc context. 111 | * @return 0 on success, negative on error 112 | */ 113 | int esc_db_destroy(esc_context * esc_ctx_p); 114 | 115 | 116 | /** 117 | * Sets the value of a property in the database's "settings" table. 118 | * 119 | * @param name The name of the property. 120 | * @param status The int value of the property. 121 | * @param esc_ctx_p Pointer to the axc context. 122 | * @return 0 on success, negative on error 123 | */ 124 | int esc_db_property_set(const char * name, const int val, esc_context * esc_ctx_p); 125 | 126 | /** 127 | * Gets a property from the settings table. 128 | * 129 | * @param name Name of the property 130 | * @param val_p Pointer to where the saved value should be stored. 131 | * @param esc_ctx_p Pointer to the axc context. 132 | * @return 0 on success, negative on error, 1 if no sql error but no result 133 | */ 134 | int esc_db_property_get(const char * name, int * val_p, esc_context * esc_ctx_p); 135 | 136 | /** 137 | * "Partial application" of db_set_property, setting the init status value. 138 | * 139 | * @param status AXC_DB_NOT INITIALIZED, AXC_DB_NEEDS_ROOLBACK, or AXC_DB_INITIALIZED 140 | * @param esc_ctx_p Pointer to the axc context. 141 | * @return 0 on success, negative on error 142 | */ 143 | int esc_db_init_status_set(const int status, esc_context * esc_ctx_p); 144 | 145 | /** 146 | * "Partial application" of db_get_property, getting the init status value. 147 | * 148 | * @param init_status_p The value behind this pointer will be set to the init status number. 149 | * @param esc_ctx_p Pointer to the axc context. 150 | * @return 0 on success, negative on error, 1 if no sql error but no result 151 | */ 152 | int esc_db_init_status_get(int * init_status_p, esc_context * esc_ctx_p); 153 | -------------------------------------------------------------------------------- /c_src/libsignal-c: -------------------------------------------------------------------------------- 1 | ../libs/libsignal-protocol-c/src -------------------------------------------------------------------------------- /erl_signal: -------------------------------------------------------------------------------- 1 | [editor] 2 | line_wrapping=false 3 | line_break_column=72 4 | auto_continue_multiline=true 5 | 6 | [file_prefs] 7 | final_new_line=true 8 | ensure_convert_new_lines=true 9 | strip_trailing_spaces=true 10 | replace_tabs=true 11 | 12 | [indentation] 13 | indent_width=4 14 | indent_type=1 15 | indent_hard_tab_width=8 16 | detect_indent=false 17 | detect_indent_width=false 18 | indent_mode=2 19 | 20 | [project] 21 | name=erl_signal/ 22 | base_path=/home/begemot/erl/apdu/erl_signal/ 23 | description= 24 | file_patterns= 25 | 26 | [long line marker] 27 | long_line_behaviour=1 28 | long_line_column=72 29 | 30 | [files] 31 | current_page=1 32 | FILE_NAME_0=0;C;0;EUTF-8;1;1;0;%2Fhome%2Fbegemot%2Ferl%2Fapdu%2Ferl_signal%2Fc_src%2Faxc.c;0;4 33 | FILE_NAME_1=258;C++;0;EUTF-8;1;1;0;%2Fhome%2Fbegemot%2Ferl%2Fapdu%2Ferl_signal%2Fc_src%2Flibsignal-c%2Fsignal_protocol.h;0;4 34 | -------------------------------------------------------------------------------- /erl_signal.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 421 | 422 | -------------------------------------------------------------------------------- /erl_signal.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /include/erl_signal.hrl: -------------------------------------------------------------------------------- 1 | -record(es_address, { 2 | name :: binary(), 3 | device_id :: integer() 4 | }). 5 | 6 | -define(IS_ES_ADDRESS(X), is_record(X, es_address)). 7 | 8 | 9 | -------------------------------------------------------------------------------- /libs/.gitignore: -------------------------------------------------------------------------------- 1 | /libsignal-protocol-c 2 | /cJSON 3 | 4 | -------------------------------------------------------------------------------- /libs/Makefile: -------------------------------------------------------------------------------- 1 | all: libsignal-protocol-c/.compiled cJSON/.compiled 2 | 3 | 4 | libsignal-protocol-c/.get: 5 | git clone https://github.com/signalapp/libsignal-protocol-c.git \ 6 | && cd libsignal-protocol-c \ 7 | && git checkout v2.3.2 \ 8 | && sed -n -i 'p;41a SET(CMAKE_C_FLAGS "\x24\{CMAKE_C_FLAGS} -fPIC")' CMakeLists.txt \ 9 | && touch .get 10 | 11 | libsignal-protocol-c/.compiled: libsignal-protocol-c/.get 12 | cd libsignal-protocol-c \ 13 | && mkdir -p build \ 14 | && cd build \ 15 | && cmake -DCMAKE_BUILD_TYPE=Debug .. \ 16 | && make \ 17 | && touch ../.compiled 18 | 19 | cJSON/.get: 20 | git clone https://github.com/DaveGamble/cJSON.git \ 21 | && touch cJSON/.get 22 | 23 | cJSON/.compiled: cJSON/.get 24 | cd cJSON \ 25 | && make \ 26 | && touch .compiled 27 | 28 | -------------------------------------------------------------------------------- /libs/axc/axc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // va_* 3 | #include // printf, fprintf 4 | #include // exit, malloc 5 | #include // memset 6 | 7 | #ifndef NO_THREADS 8 | #include // mutex stuff 9 | #endif 10 | 11 | #include 12 | 13 | #include "signal_protocol.h" 14 | #include "key_helper.h" 15 | #include "protocol.h" 16 | #include "session_builder.h" 17 | #include "session_builder_internal.h" 18 | #include "session_cipher.h" 19 | #include "session_state.h" 20 | 21 | #include "axc.h" 22 | #include "axc_store.h" 23 | #include "axc_crypto.h" 24 | 25 | void recursive_mutex_lock(void * user_data); 26 | void recursive_mutex_unlock(void * user_data); 27 | 28 | typedef struct axc_mutexes { 29 | #ifndef NO_THREADS 30 | pthread_mutex_t * mutex_p; 31 | pthread_mutexattr_t * mutex_attr_p; 32 | #endif 33 | } axc_mutexes; 34 | 35 | struct axc_context { 36 | signal_context * axolotl_global_context_p; 37 | signal_protocol_store_context * axolotl_store_context_p; 38 | axc_mutexes * mutexes_p; 39 | char * db_filename; 40 | void (*log_func)(int level, const char * message, size_t len, void * user_data); 41 | int log_level; 42 | }; 43 | 44 | struct axc_handshake { 45 | session_builder * session_builder_p; 46 | axc_buf * handshake_msg_p; 47 | }; 48 | 49 | struct axc_buf_list_item { 50 | uint32_t id; 51 | axc_buf * buf_p; 52 | axc_buf_list_item * next_p; 53 | }; 54 | 55 | struct axc_bundle { 56 | uint32_t registration_id; 57 | axc_buf_list_item * pre_keys_head_p; 58 | uint32_t signed_pre_key_id; 59 | axc_buf * signed_pre_key_public_serialized_p; 60 | axc_buf * signed_pre_key_signature_p; 61 | axc_buf * identity_key_public_serialized_p; 62 | }; 63 | 64 | int axc_buf_list_item_create(axc_buf_list_item ** item_pp, uint32_t * id_p, axc_buf * data_p) { 65 | axc_buf_list_item * item_p = malloc(sizeof(axc_buf_list_item)); 66 | if (!item_p) { 67 | return -1; 68 | } 69 | memset(item_p, 0, sizeof(axc_buf_list_item)); 70 | 71 | if (id_p) { 72 | item_p->id = *id_p; 73 | } 74 | if (data_p) { 75 | item_p->buf_p = data_p; 76 | } 77 | 78 | *item_pp = item_p; 79 | return 0; 80 | } 81 | 82 | void axc_buf_list_item_set_next(axc_buf_list_item * item_p, axc_buf_list_item * next_p) { 83 | item_p->next_p = next_p; 84 | } 85 | 86 | axc_buf_list_item * axc_buf_list_item_get_next(axc_buf_list_item * item_p) { 87 | return item_p->next_p; 88 | } 89 | 90 | uint32_t axc_buf_list_item_get_id(axc_buf_list_item * item_p) { 91 | return item_p->id; 92 | } 93 | 94 | axc_buf * axc_buf_list_item_get_buf(axc_buf_list_item * item_p) { 95 | return item_p->buf_p; 96 | } 97 | 98 | void axc_buf_list_free(axc_buf_list_item * head_p) { 99 | axc_buf_list_item * next = head_p; 100 | axc_buf_list_item * temp = (void *) 0; 101 | 102 | while (next) { 103 | axc_buf_free(next->buf_p); 104 | temp = next->next_p; 105 | free(next); 106 | next = temp; 107 | } 108 | } 109 | 110 | int axc_bundle_collect(size_t n, axc_context * ctx_p, axc_bundle ** bundle_pp) { 111 | int ret_val = 0; 112 | char * err_msg = ""; 113 | 114 | axc_bundle * bundle_p = (void *) 0; 115 | uint32_t reg_id = 0; 116 | axc_buf_list_item * pre_key_list_p = (void *) 0; 117 | uint32_t signed_prekey_id = 0; //FIXME: right now, only one is ever generated, this should be changed 118 | session_signed_pre_key * signed_prekey_p = (void *) 0; 119 | ec_key_pair * signed_prekey_pair_p = (void *) 0; 120 | ec_public_key * signed_prekey_public_p = (void *) 0; 121 | axc_buf * signed_prekey_public_data_p = (void *) 0; 122 | axc_buf * signed_prekey_signature_data_p = (void *) 0; 123 | ratchet_identity_key_pair * identity_key_pair_p = (void *) 0; 124 | ec_public_key * identity_key_public_p = (void *) 0; 125 | axc_buf * identity_key_public_data_p = (void *) 0; 126 | 127 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: entered", __func__); 128 | 129 | bundle_p = malloc(sizeof(axc_bundle)); 130 | if (!bundle_p) { 131 | err_msg = "failed to malloc bundle"; 132 | ret_val = AXC_ERR_NOMEM; 133 | goto cleanup; 134 | } 135 | memset(bundle_p, 0, sizeof(axc_bundle)); 136 | 137 | ret_val = axc_get_device_id(ctx_p, ®_id); 138 | if (ret_val) { 139 | err_msg = "failed to retrieve device id"; 140 | goto cleanup; 141 | } 142 | bundle_p->registration_id = reg_id; 143 | 144 | ret_val = axc_db_pre_key_get_list(n, ctx_p, &pre_key_list_p); 145 | if (ret_val) { 146 | err_msg = "failed to retrieve pre key list"; 147 | goto cleanup; 148 | } 149 | bundle_p->pre_keys_head_p = pre_key_list_p; 150 | 151 | ret_val = signal_protocol_signed_pre_key_load_key(ctx_p->axolotl_store_context_p, &signed_prekey_p, signed_prekey_id); 152 | if (ret_val) { 153 | err_msg = "failed to get signed pre key"; 154 | goto cleanup; 155 | } 156 | signed_prekey_pair_p = session_signed_pre_key_get_key_pair(signed_prekey_p); 157 | signed_prekey_public_p = ec_key_pair_get_public(signed_prekey_pair_p); 158 | 159 | ret_val = ec_public_key_serialize(&signed_prekey_public_data_p, signed_prekey_public_p); 160 | if (ret_val) { 161 | err_msg = "failed to serialize signed pre key"; 162 | goto cleanup; 163 | } 164 | bundle_p->signed_pre_key_public_serialized_p = signed_prekey_public_data_p; 165 | 166 | signed_prekey_signature_data_p = axc_buf_create(session_signed_pre_key_get_signature(signed_prekey_p), 167 | session_signed_pre_key_get_signature_len(signed_prekey_p)); 168 | if (!signed_prekey_signature_data_p) { 169 | ret_val = AXC_ERR; 170 | err_msg = "failed to create buffer for signature data"; 171 | goto cleanup; 172 | } 173 | bundle_p->signed_pre_key_signature_p = signed_prekey_signature_data_p; 174 | 175 | ret_val = signal_protocol_identity_get_key_pair(ctx_p->axolotl_store_context_p, &identity_key_pair_p); 176 | if (ret_val) { 177 | err_msg = "failed to retrieve identity key pair"; 178 | goto cleanup; 179 | } 180 | identity_key_public_p = ratchet_identity_key_pair_get_public(identity_key_pair_p); 181 | 182 | ret_val = ec_public_key_serialize(&identity_key_public_data_p, identity_key_public_p); 183 | if (ret_val) { 184 | err_msg = "failed to serialize identity key"; 185 | goto cleanup; 186 | } 187 | bundle_p->identity_key_public_serialized_p = identity_key_public_data_p; 188 | 189 | *bundle_pp = bundle_p; 190 | 191 | cleanup: 192 | if (ret_val) { 193 | axc_buf_list_free(pre_key_list_p); 194 | axc_buf_free(signed_prekey_public_data_p); 195 | axc_buf_free(signed_prekey_signature_data_p); 196 | axc_buf_free(identity_key_public_data_p); 197 | free(bundle_p); 198 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 199 | } 200 | 201 | SIGNAL_UNREF(signed_prekey_p); 202 | SIGNAL_UNREF(identity_key_pair_p); 203 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: leaving", __func__); 204 | return ret_val; 205 | } 206 | 207 | uint32_t axc_bundle_get_reg_id(axc_bundle * bundle_p) { 208 | return bundle_p->registration_id; 209 | } 210 | 211 | axc_buf_list_item * axc_bundle_get_pre_key_list(axc_bundle * bundle_p) { 212 | return bundle_p->pre_keys_head_p; 213 | } 214 | 215 | uint32_t axc_bundle_get_signed_pre_key_id(axc_bundle * bundle_p) { 216 | return bundle_p->signed_pre_key_id; 217 | } 218 | 219 | axc_buf * axc_bundle_get_signed_pre_key(axc_bundle * bundle_p) { 220 | return bundle_p->signed_pre_key_public_serialized_p; 221 | } 222 | 223 | axc_buf * axc_bundle_get_signature(axc_bundle * bundle_p) { 224 | return bundle_p->signed_pre_key_signature_p; 225 | } 226 | 227 | axc_buf * axc_bundle_get_identity_key(axc_bundle * bundle_p) { 228 | return bundle_p->identity_key_public_serialized_p; 229 | } 230 | 231 | void axc_bundle_destroy(axc_bundle * bundle_p) { 232 | if (bundle_p) { 233 | axc_buf_list_free(bundle_p->pre_keys_head_p); 234 | axc_buf_free(bundle_p->signed_pre_key_public_serialized_p); 235 | axc_buf_free(bundle_p->signed_pre_key_signature_p); 236 | axc_buf_free(bundle_p->identity_key_public_serialized_p); 237 | } 238 | } 239 | 240 | void axc_default_log(int level, const char *message, size_t len, void *user_data) { 241 | (void) len; 242 | 243 | axc_context * ctx_p = (axc_context *) user_data; 244 | 245 | if (ctx_p->log_level >= AXC_LOG_ERROR) { 246 | switch(level) { 247 | case AXC_LOG_ERROR: 248 | fprintf(stderr, "[AXC ERROR] %s\n", message); 249 | break; 250 | case AXC_LOG_WARNING: 251 | if (ctx_p->log_level >= AXC_LOG_WARNING) { 252 | fprintf(stderr, "[AXC WARNING] %s\n", message); 253 | } 254 | break; 255 | case AXC_LOG_NOTICE: 256 | if (ctx_p->log_level >= AXC_LOG_NOTICE) { 257 | fprintf(stderr, "[AXC NOTICE] %s\n", message); 258 | } 259 | break; 260 | case AXC_LOG_INFO: 261 | if (ctx_p->log_level >= AXC_LOG_INFO) { 262 | fprintf(stdout, "[AXC INFO] %s\n", message); 263 | } 264 | break; 265 | case AXC_LOG_DEBUG: 266 | if (ctx_p->log_level >= AXC_LOG_DEBUG) { 267 | fprintf(stdout, "[AXC DEBUG] %s\n", message); 268 | } 269 | break; 270 | default: 271 | if (ctx_p->log_level > AXC_LOG_DEBUG) { 272 | fprintf(stderr, "[AXC %d] %s\n", level, message); 273 | } 274 | break; 275 | } 276 | } 277 | } 278 | 279 | void axc_log(axc_context * ctx_p, int level, const char * format, ...) { 280 | if(ctx_p->log_func) { 281 | va_list args; 282 | va_list args_cpy; 283 | va_copy(args_cpy, args); 284 | 285 | va_start(args, format); 286 | size_t len = vsnprintf((void *) 0, 0, format, args) + 1; 287 | va_end(args); 288 | 289 | char msg[len]; 290 | va_start(args_cpy, format); 291 | size_t final_len = vsnprintf(msg, len, format, args_cpy); 292 | va_end(args_cpy); 293 | if(final_len > 0) { 294 | ctx_p->log_func(level, msg, len, ctx_p); 295 | } 296 | } 297 | } 298 | 299 | int axc_mutexes_create_and_init(axc_mutexes ** mutexes_pp) { 300 | #ifndef NO_THREADS 301 | axc_mutexes * mutexes_p = malloc(sizeof(axc_mutexes)); 302 | if (!mutexes_p) { 303 | return -1; 304 | } 305 | memset(mutexes_p, 0, sizeof(axc_mutexes)); 306 | *mutexes_pp = mutexes_p; 307 | 308 | pthread_mutex_t * mutex_p = malloc(sizeof(pthread_mutex_t)); 309 | if (!mutex_p) { 310 | return -2; 311 | } 312 | mutexes_p->mutex_p = mutex_p; 313 | 314 | pthread_mutexattr_t * mutex_attr_p = malloc(sizeof(pthread_mutexattr_t)); 315 | if (!mutex_attr_p) { 316 | return -3; 317 | } 318 | mutexes_p->mutex_attr_p = mutex_attr_p; 319 | 320 | if (pthread_mutexattr_init(mutex_attr_p)) { 321 | return -4; 322 | } 323 | if (pthread_mutexattr_settype(mutex_attr_p, PTHREAD_MUTEX_RECURSIVE)) { 324 | return -5; 325 | } 326 | 327 | if (pthread_mutex_init(mutex_p, mutex_attr_p)) { 328 | return -6; 329 | } 330 | #else 331 | *mutexes_pp = (void *) 0; 332 | #endif 333 | 334 | 335 | return 0; 336 | } 337 | 338 | void axc_mutexes_destroy(axc_mutexes * mutexes_p) { 339 | #ifndef NO_THREADS 340 | if (mutexes_p) { 341 | if (mutexes_p->mutex_p) { 342 | pthread_mutex_destroy(mutexes_p->mutex_p); 343 | free(mutexes_p->mutex_p); 344 | } 345 | 346 | if (mutexes_p->mutex_attr_p) { 347 | pthread_mutexattr_destroy(mutexes_p->mutex_attr_p); 348 | free(mutexes_p->mutex_attr_p); 349 | } 350 | 351 | free(mutexes_p); 352 | } 353 | #else 354 | (void) mutexes_p; 355 | #endif 356 | } 357 | 358 | int axc_context_create(axc_context ** ctx_pp) { 359 | if (!ctx_pp) { 360 | return -1; 361 | } 362 | 363 | axc_context * ctx_p = (void *) 0; 364 | ctx_p = malloc(sizeof(axc_context)); 365 | if (!ctx_p) { 366 | return -2; 367 | } 368 | memset(ctx_p, 0, sizeof(axc_context)); 369 | 370 | ctx_p->log_level = -1; 371 | 372 | *ctx_pp = ctx_p; 373 | return 0; 374 | } 375 | 376 | int axc_context_set_db_fn(axc_context * ctx_p, char * filename, size_t fn_len) { 377 | char * db_fn = g_strndup(filename, fn_len); 378 | if (!db_fn) { 379 | return -1; 380 | } 381 | 382 | ctx_p->db_filename = db_fn; 383 | return 0; 384 | } 385 | 386 | char * axc_context_get_db_fn(axc_context * ctx_p) { 387 | if (ctx_p->db_filename) { 388 | return ctx_p->db_filename; 389 | } else { 390 | return AXC_DB_DEFAULT_FN; 391 | } 392 | } 393 | 394 | void axc_context_set_log_func(axc_context * ctx_p, void (*log_func)(int level, const char * message, size_t len, void * user_data)) { 395 | ctx_p->log_func = log_func; 396 | } 397 | 398 | void axc_context_set_log_level(axc_context * ctx_p, int level) { 399 | ctx_p->log_level = level; 400 | } 401 | 402 | int axc_context_get_log_level(axc_context * ctx_p) { 403 | return ctx_p->log_level; 404 | } 405 | 406 | signal_context * axc_context_get_axolotl_ctx(axc_context * ctx_p) { 407 | return ctx_p ? ctx_p->axolotl_global_context_p : (void *) 0; 408 | } 409 | 410 | void axc_context_destroy_all(axc_context * ctx_p) { 411 | if (ctx_p) { 412 | signal_context_destroy(ctx_p->axolotl_global_context_p); 413 | signal_protocol_store_context_destroy(ctx_p->axolotl_store_context_p); 414 | axc_mutexes_destroy(ctx_p->mutexes_p); 415 | 416 | free(ctx_p->db_filename); 417 | } 418 | } 419 | 420 | void recursive_mutex_lock(void * user_data) { 421 | #ifndef NO_THREADS 422 | axc_context * ctx_p = (axc_context *) user_data; 423 | pthread_mutex_lock(ctx_p->mutexes_p->mutex_p); 424 | #else 425 | (void) user_data; 426 | #endif 427 | } 428 | 429 | void recursive_mutex_unlock(void * user_data) { 430 | #ifndef NO_THREADS 431 | axc_context * ctx_p = (axc_context *) user_data; 432 | pthread_mutex_unlock(ctx_p->mutexes_p->mutex_p); 433 | #else 434 | (void) user_data; 435 | #endif 436 | } 437 | 438 | axc_buf * axc_buf_create(const uint8_t * data, size_t len) { 439 | return signal_buffer_create(data, len); 440 | } 441 | 442 | uint8_t * axc_buf_get_data(axc_buf * buf) { 443 | return signal_buffer_data(buf); 444 | } 445 | 446 | size_t axc_buf_get_len(axc_buf * buf) { 447 | return signal_buffer_len(buf); 448 | } 449 | 450 | void axc_buf_free(axc_buf * buf) { 451 | signal_buffer_bzero_free(buf); 452 | } 453 | 454 | int axc_init(axc_context * ctx_p) { 455 | axc_log(ctx_p, AXC_LOG_INFO, "%s: initializing axolotl client", __func__); 456 | char * err_msg = " "; 457 | int ret_val = 0; 458 | 459 | axc_mutexes * mutexes_p = (void *) 0; 460 | signal_protocol_store_context * store_context_p = (void *) 0; 461 | 462 | signal_protocol_session_store session_store = { 463 | .load_session_func = &axc_db_session_load, 464 | .get_sub_device_sessions_func = &axc_db_session_get_sub_device_sessions, 465 | .store_session_func = &axc_db_session_store, 466 | .contains_session_func = &axc_db_session_contains, 467 | .delete_session_func = &axc_db_session_delete, 468 | .delete_all_sessions_func = &axc_db_session_delete_all, 469 | .destroy_func = &axc_db_session_destroy_store_ctx, 470 | .user_data = ctx_p 471 | }; 472 | signal_protocol_pre_key_store pre_key_store = { 473 | .load_pre_key = &axc_db_pre_key_load, 474 | .store_pre_key = &axc_db_pre_key_store, 475 | .contains_pre_key = &axc_db_pre_key_contains, 476 | .remove_pre_key = &axc_db_pre_key_remove, 477 | .destroy_func = &axc_db_pre_key_destroy_ctx, 478 | .user_data = ctx_p 479 | }; 480 | signal_protocol_signed_pre_key_store signed_pre_key_store = { 481 | .load_signed_pre_key = &axc_db_signed_pre_key_load, 482 | .store_signed_pre_key = &axc_db_signed_pre_key_store, 483 | .contains_signed_pre_key = &axc_db_signed_pre_key_contains, 484 | .remove_signed_pre_key = &axc_db_signed_pre_key_remove, 485 | .destroy_func = &axc_db_signed_pre_key_destroy_ctx, 486 | .user_data = ctx_p 487 | }; 488 | signal_protocol_identity_key_store identity_key_store = { 489 | .get_identity_key_pair = &axc_db_identity_get_key_pair, 490 | .get_local_registration_id = &axc_db_identity_get_local_registration_id, 491 | .save_identity = &axc_db_identity_save, 492 | .is_trusted_identity = &axc_db_identity_always_trusted, 493 | .destroy_func = &axc_db_identity_destroy_ctx, 494 | .user_data = ctx_p 495 | }; 496 | 497 | // init mutexes 498 | ret_val = axc_mutexes_create_and_init(&mutexes_p); 499 | if (ret_val) { 500 | err_msg = "failed to create or init mutexes"; 501 | goto cleanup; 502 | } 503 | ctx_p->mutexes_p = mutexes_p; 504 | 505 | // axolotl lib init 506 | // 1. create global context 507 | if (signal_context_create(&(ctx_p->axolotl_global_context_p), ctx_p)) { 508 | err_msg = "failed to create global axolotl context"; 509 | ret_val = -1; 510 | goto cleanup; 511 | } 512 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: created and set axolotl context", __func__); 513 | 514 | // 2. init and set crypto provider 515 | signal_crypto_provider crypto_provider = { 516 | .random_func = random_bytes, 517 | .hmac_sha256_init_func = hmac_sha256_init, 518 | .hmac_sha256_update_func = hmac_sha256_update, 519 | .hmac_sha256_final_func = hmac_sha256_final, 520 | .hmac_sha256_cleanup_func = hmac_sha256_cleanup, 521 | .sha512_digest_init_func = sha512_digest_init, 522 | .sha512_digest_update_func = sha512_digest_update, 523 | .sha512_digest_final_func = sha512_digest_final, 524 | .sha512_digest_cleanup_func = sha512_digest_cleanup, 525 | .encrypt_func = aes_encrypt, 526 | .decrypt_func = aes_decrypt, 527 | .user_data = ctx_p 528 | }; 529 | if (signal_context_set_crypto_provider(ctx_p->axolotl_global_context_p, &crypto_provider)) { 530 | err_msg = "failed to set crypto provider"; 531 | ret_val = -1; 532 | goto cleanup; 533 | } 534 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: set axolotl crypto provider", __func__); 535 | 536 | // 3. set locking functions 537 | #ifndef NO_THREADS 538 | if (signal_context_set_locking_functions(ctx_p->axolotl_global_context_p, recursive_mutex_lock, recursive_mutex_unlock)) { 539 | err_msg = "failed to set locking functions"; 540 | ret_val = -1; 541 | goto cleanup; 542 | } 543 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: set locking functions", __func__); 544 | #endif 545 | 546 | // init store context 547 | 548 | if (signal_protocol_store_context_create(&store_context_p, ctx_p->axolotl_global_context_p)) { 549 | err_msg = "failed to create store context"; 550 | ret_val = -1; 551 | goto cleanup; 552 | } 553 | 554 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: created store context", __func__); 555 | 556 | if (signal_protocol_store_context_set_session_store(store_context_p, &session_store)) { 557 | err_msg = "failed to create session store"; 558 | ret_val = -1; 559 | goto cleanup; 560 | } 561 | 562 | if (signal_protocol_store_context_set_pre_key_store(store_context_p, &pre_key_store)) { 563 | err_msg = "failed to set pre key store"; 564 | ret_val = -1; 565 | goto cleanup; 566 | } 567 | 568 | if (signal_protocol_store_context_set_signed_pre_key_store(store_context_p, &signed_pre_key_store)) { 569 | err_msg = "failed to set signed pre key store"; 570 | ret_val = -1; 571 | goto cleanup; 572 | } 573 | 574 | if (signal_protocol_store_context_set_identity_key_store(store_context_p, &identity_key_store)) { 575 | err_msg = "failed to set identity key store"; 576 | ret_val = -1; 577 | goto cleanup; 578 | } 579 | 580 | ctx_p->axolotl_store_context_p = store_context_p; 581 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: set store context", __func__); 582 | 583 | cleanup: 584 | if (ret_val < 0) { 585 | //FIXME: this frees inited context, make this more fine-grained 586 | axc_cleanup(ctx_p); 587 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 588 | } else { 589 | axc_log(ctx_p, AXC_LOG_INFO, "%s: done initializing axc", __func__); 590 | } 591 | return ret_val; 592 | } 593 | 594 | void axc_cleanup(axc_context * ctx_p) { 595 | axc_context_destroy_all(ctx_p); 596 | } 597 | 598 | int axc_install(axc_context * ctx_p) { 599 | char * err_msg = ""; 600 | int ret_val = 0; 601 | int db_needs_init = 0; 602 | 603 | signal_context * global_context_p = ctx_p->axolotl_global_context_p; 604 | ratchet_identity_key_pair * identity_key_pair_p = (void *) 0; 605 | signal_protocol_key_helper_pre_key_list_node * pre_keys_head_p = (void *) 0; 606 | session_pre_key * last_resort_key_p = (void *) 0; 607 | session_signed_pre_key * signed_pre_key_p = (void *) 0; 608 | signal_buffer * last_resort_key_buf_p = (void *) 0; 609 | signal_buffer * signed_pre_key_data_p = (void *) 0; 610 | uint32_t registration_id; 611 | 612 | axc_log(ctx_p, AXC_LOG_INFO, "%s: calling install-time functions", __func__); 613 | 614 | ret_val = axc_db_create(ctx_p); 615 | if (ret_val){ 616 | err_msg = "failed to create db"; 617 | goto cleanup; 618 | } 619 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: created db if it did not exist already", __func__); 620 | 621 | int init_status = AXC_DB_NOT_INITIALIZED; 622 | int db_needs_reset = 0; 623 | ret_val = axc_db_init_status_get(&init_status, ctx_p); 624 | switch (ret_val) { 625 | case -1: 626 | default: 627 | err_msg = "failed to read init status"; 628 | goto cleanup; 629 | break; 630 | case 0: 631 | // there is a value 632 | switch (init_status) { 633 | case AXC_DB_NOT_INITIALIZED: 634 | // init needed 635 | db_needs_init = 1; 636 | break; 637 | case AXC_DB_NEEDS_ROLLBACK: 638 | // reset and init needed 639 | db_needs_reset = 1; 640 | db_needs_init = 1; 641 | break; 642 | case AXC_DB_INITIALIZED: 643 | default: 644 | // the db is already initialised 645 | break; 646 | } 647 | break; 648 | case 1: 649 | // no value = not initialised -> init needed 650 | db_needs_init = 1; 651 | break; 652 | } 653 | 654 | if (db_needs_reset) { 655 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: db needs reset", __func__ ); 656 | ret_val = axc_db_destroy(ctx_p); 657 | if (ret_val) { 658 | err_msg = "failed to reset db"; 659 | goto cleanup; 660 | } 661 | 662 | ret_val = axc_db_create(ctx_p); 663 | if (ret_val) { 664 | err_msg = "failed to create db after reset"; 665 | goto cleanup; 666 | } 667 | } else { 668 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: db does not need reset", __func__ ); 669 | } 670 | 671 | if (db_needs_init) { 672 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: db needs init", __func__ ); 673 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: setting init status to AXC_DB_NEEDS_ROLLBACK (%i)", __func__, AXC_DB_NEEDS_ROLLBACK ); 674 | 675 | ret_val = axc_db_init_status_set(AXC_DB_NEEDS_ROLLBACK, ctx_p); 676 | if (ret_val) { 677 | err_msg = "failed to set init status to AXC_DB_NEEDS_ROLLBACK"; 678 | goto cleanup; 679 | } 680 | 681 | ret_val = signal_protocol_key_helper_generate_identity_key_pair(&identity_key_pair_p, global_context_p); 682 | if (ret_val) { 683 | err_msg = "failed to generate the identity key pair"; 684 | goto cleanup; 685 | } 686 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: generated identity key pair", __func__ ); 687 | 688 | ret_val = signal_protocol_key_helper_generate_registration_id(®istration_id, 1, global_context_p); 689 | if (ret_val) { 690 | err_msg = "failed to generate registration id"; 691 | goto cleanup; 692 | } 693 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: generated registration id: %i", __func__, registration_id); 694 | 695 | ret_val = signal_protocol_key_helper_generate_pre_keys(&pre_keys_head_p, 1, AXC_PRE_KEYS_AMOUNT, global_context_p); 696 | if(ret_val) { 697 | err_msg = "failed to generate pre keys"; 698 | goto cleanup; 699 | } 700 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: generated pre keys", __func__ ); 701 | 702 | ret_val = signal_protocol_key_helper_generate_last_resort_pre_key(&last_resort_key_p, global_context_p); 703 | if (ret_val) { 704 | err_msg = "failed to generate last resort pre key"; 705 | goto cleanup; 706 | } 707 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: generated last resort pre key", __func__ ); 708 | 709 | ret_val = signal_protocol_key_helper_generate_signed_pre_key(&signed_pre_key_p, identity_key_pair_p, 0, g_get_real_time(), global_context_p); 710 | if (ret_val) { 711 | err_msg = "failed to generate signed pre key"; 712 | goto cleanup; 713 | } 714 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: generated signed pre key", __func__ ); 715 | 716 | 717 | ret_val = axc_db_identity_set_key_pair(identity_key_pair_p, ctx_p); 718 | if (ret_val) { 719 | err_msg = "failed to set identity key pair"; 720 | goto cleanup; 721 | } 722 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: saved identity key pair", __func__ ); 723 | 724 | ret_val = axc_db_identity_set_local_registration_id(registration_id, ctx_p); 725 | if (ret_val) { 726 | err_msg = "failed to set registration id"; 727 | goto cleanup; 728 | } 729 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: saved registration id", __func__ ); 730 | 731 | ret_val = axc_db_pre_key_store_list(pre_keys_head_p, ctx_p); 732 | if (ret_val) { 733 | err_msg = "failed to save pre key list"; 734 | goto cleanup; 735 | } 736 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: saved pre keys", __func__ ); 737 | 738 | 739 | ret_val = session_pre_key_serialize(&last_resort_key_buf_p, last_resort_key_p); 740 | if (ret_val) { 741 | err_msg = "failed to serialize last resort pre key"; 742 | goto cleanup; 743 | } 744 | 745 | ret_val = axc_db_pre_key_store(session_pre_key_get_id(last_resort_key_p), signal_buffer_data(last_resort_key_buf_p), signal_buffer_len(last_resort_key_buf_p), ctx_p); 746 | if (ret_val) { 747 | err_msg = "failed to save last resort pre key"; 748 | goto cleanup; 749 | } 750 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: saved last resort pre key", __func__ ); 751 | 752 | ret_val = session_signed_pre_key_serialize(&signed_pre_key_data_p, signed_pre_key_p); 753 | if (ret_val) { 754 | err_msg = "failed to serialize signed pre key"; 755 | goto cleanup; 756 | } 757 | 758 | ret_val = axc_db_signed_pre_key_store(session_signed_pre_key_get_id(signed_pre_key_p), signal_buffer_data(signed_pre_key_data_p), signal_buffer_len(signed_pre_key_data_p), ctx_p); 759 | if (ret_val) { 760 | err_msg = "failed to save signed pre key"; 761 | goto cleanup; 762 | } 763 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: saved signed pre key", __func__ ); 764 | 765 | ret_val = axc_db_init_status_set(AXC_DB_INITIALIZED, ctx_p); 766 | if (ret_val) { 767 | err_msg = "failed to set init status to AXC_DB_INITIALIZED"; 768 | goto cleanup; 769 | } 770 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: initialised DB", __func__ ); 771 | 772 | } else { 773 | axc_log(ctx_p, AXC_LOG_DEBUG, "%s: db already initialized", __func__ ); 774 | } 775 | 776 | cleanup: 777 | if (ret_val < 0) { 778 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 779 | } 780 | 781 | if (db_needs_init) { 782 | SIGNAL_UNREF(identity_key_pair_p); 783 | signal_protocol_key_helper_key_list_free(pre_keys_head_p); 784 | SIGNAL_UNREF(last_resort_key_p); 785 | SIGNAL_UNREF(signed_pre_key_p); 786 | signal_buffer_bzero_free(last_resort_key_buf_p); 787 | signal_buffer_bzero_free(signed_pre_key_data_p); 788 | } 789 | 790 | return ret_val; 791 | } 792 | 793 | int axc_get_device_id(axc_context * ctx_p, uint32_t * id_p) { 794 | return signal_protocol_identity_get_local_registration_id(ctx_p->axolotl_store_context_p, id_p); 795 | } 796 | 797 | int axc_message_encrypt_and_serialize(axc_buf * msg_p, const axc_address * recipient_addr_p, axc_context * ctx_p, axc_buf ** ciphertext_pp) { 798 | char * err_msg = ""; 799 | int ret_val = 0; 800 | 801 | session_cipher * cipher_p = (void *) 0; 802 | ciphertext_message * cipher_msg_p = (void *) 0; 803 | signal_buffer * cipher_msg_data_p = (void *) 0; 804 | axc_buf * cipher_msg_data_cpy_p = (void *) 0; 805 | 806 | if (!ctx_p) { 807 | fprintf(stderr, "%s: axc ctx is null!\n", __func__); 808 | return -1; 809 | } 810 | 811 | if (!msg_p) { 812 | err_msg = "could not encrypt because msg pointer is null"; 813 | ret_val = -1; 814 | goto cleanup; 815 | } 816 | if (!recipient_addr_p) { 817 | err_msg = "could not encrypt because recipient addr pointer is null"; 818 | ret_val = -1; 819 | goto cleanup; 820 | } 821 | if (!ciphertext_pp) { 822 | err_msg = "could not encrypt because ciphertext pointer is null"; 823 | ret_val = -1; 824 | goto cleanup; 825 | } 826 | 827 | 828 | ret_val = session_cipher_create(&cipher_p, ctx_p->axolotl_store_context_p, recipient_addr_p, ctx_p->axolotl_global_context_p); 829 | if (ret_val) { 830 | err_msg = "failed to create session cipher"; 831 | goto cleanup; 832 | } 833 | 834 | ret_val = session_cipher_encrypt(cipher_p, axc_buf_get_data(msg_p), axc_buf_get_len(msg_p), &cipher_msg_p); 835 | if (ret_val) { 836 | err_msg = "failed to encrypt the message"; 837 | goto cleanup; 838 | } 839 | 840 | cipher_msg_data_p = ciphertext_message_get_serialized(cipher_msg_p); 841 | cipher_msg_data_cpy_p = signal_buffer_copy(cipher_msg_data_p); 842 | 843 | if (!cipher_msg_data_cpy_p) { 844 | err_msg = "failed to copy cipher msg data"; 845 | ret_val = -1; 846 | goto cleanup; 847 | } 848 | 849 | *ciphertext_pp = cipher_msg_data_cpy_p; 850 | 851 | cleanup: 852 | if (ret_val < 0) { 853 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 854 | axc_buf_free(cipher_msg_data_cpy_p); 855 | } 856 | 857 | session_cipher_free(cipher_p); 858 | SIGNAL_UNREF(cipher_msg_p); 859 | 860 | return ret_val; 861 | } 862 | 863 | int axc_message_decrypt_from_serialized (axc_buf * msg_p, axc_address * sender_addr_p, axc_context * ctx_p, axc_buf ** plaintext_pp) { 864 | char * err_msg = ""; 865 | int ret_val = 0; 866 | 867 | //TODO: add session_cipher_set_decryption_callback maybe? 868 | //FIXME: check message type 869 | 870 | signal_message * ciphertext_p = (void *) 0; 871 | session_cipher * cipher_p = (void *) 0; 872 | axc_buf * plaintext_buf_p = (void *) 0; 873 | 874 | if (!ctx_p) { 875 | fprintf(stderr, "%s: axc ctx is null!\n", __func__); 876 | return -1; 877 | } 878 | 879 | if (!msg_p) { 880 | err_msg = "could not decrypt because message pointer is null"; 881 | ret_val = -1; 882 | goto cleanup; 883 | } 884 | if (!sender_addr_p) { 885 | err_msg = "could not decrypt because sender address pointer is null"; 886 | ret_val = -1; 887 | goto cleanup; 888 | } 889 | if (!plaintext_pp) { 890 | err_msg = "could not decrypt because plaintext pointer is null"; 891 | ret_val = -1; 892 | goto cleanup; 893 | } 894 | 895 | ret_val = session_cipher_create(&cipher_p, ctx_p->axolotl_store_context_p, sender_addr_p, ctx_p->axolotl_global_context_p); 896 | if (ret_val) { 897 | err_msg = "failed to create session cipher"; 898 | goto cleanup; 899 | } 900 | 901 | ret_val = signal_message_deserialize(&ciphertext_p, axc_buf_get_data(msg_p), axc_buf_get_len(msg_p), ctx_p->axolotl_global_context_p); 902 | if (ret_val) { 903 | err_msg = "failed to deserialize whisper msg"; 904 | goto cleanup; 905 | } 906 | ret_val = session_cipher_decrypt_signal_message(cipher_p, ciphertext_p, (void *) 0, &plaintext_buf_p); 907 | if (ret_val) { 908 | err_msg = "failed to decrypt cipher message"; 909 | goto cleanup; 910 | } 911 | 912 | *plaintext_pp = plaintext_buf_p; 913 | 914 | cleanup: 915 | if (ret_val < 0) { 916 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 917 | } 918 | 919 | session_cipher_free(cipher_p); 920 | SIGNAL_UNREF(ciphertext_p); 921 | 922 | return ret_val; 923 | } 924 | 925 | int axc_session_exists_initiated(const axc_address * addr_p, axc_context * ctx_p) { 926 | int ret_val = 0; 927 | char * err_msg = ""; 928 | 929 | session_record * session_record_p = (void *) 0; 930 | session_state * session_state_p = (void *) 0; 931 | 932 | //TODO: if there was no response yet, even though it is an established session it keeps sending prekeymsgs 933 | // maybe that is "uninitiated" too? 934 | 935 | if(!signal_protocol_session_contains_session(ctx_p->axolotl_store_context_p, addr_p)) { 936 | return 0; 937 | } 938 | 939 | ret_val = signal_protocol_session_load_session(ctx_p->axolotl_store_context_p, &session_record_p, addr_p); 940 | if (ret_val){ 941 | err_msg = "database error when trying to retrieve session"; 942 | goto cleanup; 943 | } else { 944 | session_state_p = session_record_get_state(session_record_p); 945 | if (session_state_has_pending_key_exchange(session_state_p)) { 946 | err_msg = "session exists but has pending synchronous key exchange"; 947 | ret_val = 0; 948 | goto cleanup; 949 | } 950 | 951 | ret_val = 1; 952 | } 953 | 954 | cleanup: 955 | if (ret_val < 1) { 956 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 957 | } 958 | 959 | SIGNAL_UNREF(session_record_p); 960 | return ret_val; 961 | } 962 | 963 | /** 964 | * Checks if there exists a session for a user. 965 | * 966 | * @param name The username. 967 | * @param ctx_p Pointer to the axc context. 968 | * @return 0 if no session exists, 1 if at least one session exists, negative on error. 969 | */ 970 | int axc_session_exists_any(const char * name, axc_context * ctx_p) { 971 | int ret_val = 0; 972 | 973 | signal_int_list * sess_l_p = (void *) 0; 974 | 975 | ret_val = signal_protocol_session_get_sub_device_sessions(ctx_p->axolotl_store_context_p, &sess_l_p, name, strlen(name)); 976 | if (ret_val < 0) { 977 | goto cleanup; 978 | } 979 | 980 | ret_val = (signal_int_list_size(sess_l_p) > 0) ? 1 : 0; 981 | 982 | cleanup: 983 | signal_int_list_free(sess_l_p); 984 | return ret_val; 985 | } 986 | 987 | 988 | int axc_session_from_bundle(uint32_t pre_key_id, 989 | axc_buf * pre_key_public_serialized_p, 990 | uint32_t signed_pre_key_id, 991 | axc_buf * signed_pre_key_public_serialized_p, 992 | axc_buf * signed_pre_key_signature_p, 993 | axc_buf * identity_key_public_serialized_p, 994 | const axc_address * remote_address_p, 995 | axc_context * ctx_p) { 996 | 997 | char * err_msg = ""; 998 | int ret_val = 0; 999 | 1000 | ec_public_key * pre_key_public_p = (void *) 0; 1001 | ec_public_key * signed_pre_key_public_p = (void *) 0; 1002 | ec_public_key * identity_key_public_p = (void *) 0; 1003 | session_pre_key_bundle * bundle_p = (void *) 0; 1004 | session_builder * session_builder_p = (void *) 0; 1005 | 1006 | ret_val = curve_decode_point(&pre_key_public_p, 1007 | axc_buf_get_data(pre_key_public_serialized_p), 1008 | axc_buf_get_len(pre_key_public_serialized_p), 1009 | ctx_p->axolotl_global_context_p); 1010 | if (ret_val) { 1011 | err_msg = "failed to deserialize public pre key"; 1012 | goto cleanup; 1013 | } 1014 | 1015 | 1016 | ret_val = curve_decode_point(&signed_pre_key_public_p, 1017 | axc_buf_get_data(signed_pre_key_public_serialized_p), 1018 | axc_buf_get_len(signed_pre_key_public_serialized_p), 1019 | ctx_p->axolotl_global_context_p); 1020 | if (ret_val) { 1021 | err_msg = "failed to deserialize signed public pre key"; 1022 | goto cleanup; 1023 | } 1024 | 1025 | ret_val = curve_decode_point(&identity_key_public_p, 1026 | axc_buf_get_data(identity_key_public_serialized_p), 1027 | axc_buf_get_len(identity_key_public_serialized_p), 1028 | ctx_p->axolotl_global_context_p); 1029 | if (ret_val) { 1030 | err_msg = "failed to deserialize public identity key"; 1031 | goto cleanup; 1032 | } 1033 | 1034 | ret_val = session_pre_key_bundle_create(&bundle_p, 1035 | remote_address_p->device_id, 1036 | remote_address_p->device_id, // this value is ignored 1037 | pre_key_id, 1038 | pre_key_public_p, 1039 | signed_pre_key_id, 1040 | signed_pre_key_public_p, 1041 | axc_buf_get_data(signed_pre_key_signature_p), 1042 | axc_buf_get_len(signed_pre_key_signature_p), 1043 | identity_key_public_p); 1044 | if (ret_val) { 1045 | err_msg = "failed to assemble bundle"; 1046 | goto cleanup; 1047 | } 1048 | 1049 | ret_val = session_builder_create(&session_builder_p, ctx_p->axolotl_store_context_p, remote_address_p, ctx_p->axolotl_global_context_p); 1050 | if (ret_val) { 1051 | err_msg = "failed to create session builder"; 1052 | goto cleanup; 1053 | } 1054 | 1055 | ret_val = session_builder_process_pre_key_bundle(session_builder_p, bundle_p); 1056 | if (ret_val) { 1057 | err_msg = "failed to process pre key bundle"; 1058 | goto cleanup; 1059 | } 1060 | 1061 | cleanup: 1062 | if (ret_val) { 1063 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 1064 | } 1065 | 1066 | SIGNAL_UNREF(pre_key_public_p); 1067 | SIGNAL_UNREF(signed_pre_key_public_p); 1068 | SIGNAL_UNREF(identity_key_public_p); 1069 | SIGNAL_UNREF(bundle_p); 1070 | session_builder_free(session_builder_p); 1071 | 1072 | return ret_val; 1073 | } 1074 | 1075 | int axc_session_delete(const char * user, uint32_t device_id, axc_context * ctx_p) { 1076 | int ret_val = 0; 1077 | 1078 | axc_address addr = {.name = user, .name_len = strlen(user), .device_id = device_id}; 1079 | ret_val = signal_protocol_session_delete_session(ctx_p->axolotl_store_context_p, &addr); 1080 | if (ret_val) { 1081 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: failed to delete session for %s:%i", __func__, user, device_id); 1082 | } 1083 | 1084 | return ret_val; 1085 | } 1086 | 1087 | int axc_pre_key_message_process(axc_buf * pre_key_msg_serialized_p, axc_address * remote_address_p, axc_context * ctx_p, axc_buf ** plaintext_pp) { 1088 | char * err_msg = ""; 1089 | int ret_val = 0; 1090 | 1091 | session_builder * session_builder_p = (void *) 0; 1092 | session_record * session_record_p = (void *) 0; 1093 | pre_key_signal_message * pre_key_msg_p = (void *) 0; 1094 | uint32_t new_id = 0; 1095 | uint32_t pre_key_id = 0; 1096 | session_cipher * session_cipher_p = (void *) 0; 1097 | axc_buf * plaintext_p = (void *) 0; 1098 | signal_protocol_key_helper_pre_key_list_node * key_l_p = (void *) 0; 1099 | 1100 | 1101 | ret_val = session_builder_create(&session_builder_p, ctx_p->axolotl_store_context_p, remote_address_p, ctx_p->axolotl_global_context_p); 1102 | if (ret_val) { 1103 | err_msg = "failed to create session builder"; 1104 | goto cleanup; 1105 | } 1106 | 1107 | 1108 | ret_val = signal_protocol_session_load_session(ctx_p->axolotl_store_context_p, &session_record_p, remote_address_p); 1109 | if (ret_val) { 1110 | err_msg = "failed to load or create session record"; 1111 | goto cleanup; 1112 | } 1113 | 1114 | 1115 | ret_val = pre_key_signal_message_deserialize(&pre_key_msg_p, 1116 | axc_buf_get_data(pre_key_msg_serialized_p), 1117 | axc_buf_get_len(pre_key_msg_serialized_p), 1118 | ctx_p->axolotl_global_context_p); 1119 | if (ret_val == SG_ERR_INVALID_PROTO_BUF) { 1120 | err_msg = "not a pre key msg"; 1121 | ret_val = AXC_ERR_NOT_A_PREKEY_MSG; 1122 | goto cleanup; 1123 | } else if (ret_val == SG_ERR_INVALID_KEY_ID) { 1124 | ret_val = AXC_ERR_INVALID_KEY_ID; 1125 | goto cleanup; 1126 | } else if (ret_val) { 1127 | err_msg = "failed to deserialize pre key message"; 1128 | goto cleanup; 1129 | } 1130 | 1131 | ret_val = axc_db_pre_key_get_max_id(ctx_p, &new_id); 1132 | if (ret_val) { 1133 | err_msg = "failed to retrieve max pre key id"; 1134 | goto cleanup; 1135 | } 1136 | 1137 | 1138 | do { 1139 | ret_val = signal_protocol_key_helper_generate_pre_keys(&key_l_p, new_id, 1, ctx_p->axolotl_global_context_p); 1140 | if (ret_val) { 1141 | err_msg = "failed to generate a new key"; 1142 | goto cleanup; 1143 | } 1144 | 1145 | new_id++; 1146 | 1147 | } while (signal_protocol_pre_key_contains_key(ctx_p->axolotl_store_context_p, session_pre_key_get_id(signal_protocol_key_helper_key_list_element(key_l_p)))); 1148 | 1149 | 1150 | 1151 | ret_val = session_builder_process_pre_key_signal_message(session_builder_p, session_record_p, pre_key_msg_p, &pre_key_id); 1152 | if (ret_val < 0) { 1153 | err_msg = "failed to process pre key message"; 1154 | goto cleanup; 1155 | } 1156 | 1157 | 1158 | ret_val = session_cipher_create(&session_cipher_p, ctx_p->axolotl_store_context_p, remote_address_p, ctx_p->axolotl_global_context_p); 1159 | if (ret_val) { 1160 | err_msg = "failed to create session cipher"; 1161 | goto cleanup; 1162 | } 1163 | 1164 | //FIXME: find a way to retain the key (for MAM catchup) 1165 | ret_val = session_cipher_decrypt_pre_key_signal_message(session_cipher_p, pre_key_msg_p, (void *) 0, &plaintext_p); 1166 | if (ret_val) { 1167 | err_msg = "failed to decrypt message"; 1168 | goto cleanup; 1169 | } 1170 | 1171 | ret_val = signal_protocol_pre_key_store_key(ctx_p->axolotl_store_context_p, signal_protocol_key_helper_key_list_element(key_l_p)); 1172 | if (ret_val) { 1173 | err_msg = "failed to store new key"; 1174 | goto cleanup; 1175 | } 1176 | 1177 | *plaintext_pp = plaintext_p; 1178 | 1179 | cleanup: 1180 | if (ret_val < 0) { 1181 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 1182 | } 1183 | 1184 | SIGNAL_UNREF(pre_key_msg_p); 1185 | SIGNAL_UNREF(session_record_p); 1186 | SIGNAL_UNREF(session_cipher_p); 1187 | session_builder_free(session_builder_p); 1188 | signal_protocol_key_helper_key_list_free(key_l_p); 1189 | 1190 | return ret_val; 1191 | } 1192 | 1193 | int axc_key_load_public_own(axc_context * ctx_p, axc_buf ** pubkey_data_pp) { 1194 | char * err_msg = ""; 1195 | int ret_val = 0; 1196 | 1197 | ratchet_identity_key_pair * kp_p = (void *) 0; 1198 | axc_buf * key_data_p = (void *) 0; 1199 | 1200 | ret_val = signal_protocol_identity_get_key_pair(ctx_p->axolotl_store_context_p, &kp_p); 1201 | if (ret_val) { 1202 | err_msg = "failed to load identity key pair"; 1203 | goto cleanup; 1204 | } 1205 | 1206 | ret_val = ec_public_key_serialize(&key_data_p, ratchet_identity_key_pair_get_public(kp_p)); 1207 | if (ret_val) { 1208 | err_msg = "failed to serialize public identity key"; 1209 | goto cleanup; 1210 | } 1211 | 1212 | *pubkey_data_pp = key_data_p; 1213 | 1214 | cleanup: 1215 | if (ret_val) { 1216 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 1217 | axc_buf_free(key_data_p); 1218 | } 1219 | 1220 | SIGNAL_UNREF(kp_p); 1221 | 1222 | return ret_val; 1223 | } 1224 | 1225 | int axc_key_load_public_addr(const char * name, uint32_t device_id, axc_context * ctx_p, axc_buf ** pubkey_data_pp) { 1226 | char * err_msg = ""; 1227 | int ret_val = 0; 1228 | 1229 | session_record * sr_p = (void *) 0; 1230 | ec_public_key * pubkey_p = (void *) 0; 1231 | axc_buf * key_data_p = (void *) 0; 1232 | axc_address addr = {.name = name, .name_len = strlen(name), .device_id = device_id}; 1233 | 1234 | ret_val = signal_protocol_session_load_session(ctx_p->axolotl_store_context_p, &sr_p, &addr); 1235 | if (ret_val) { 1236 | err_msg = "failed to load session"; 1237 | goto cleanup; 1238 | } 1239 | 1240 | if (session_record_is_fresh(sr_p)) { 1241 | goto cleanup; 1242 | } 1243 | 1244 | ret_val = ec_public_key_serialize(&key_data_p, session_state_get_remote_identity_key(session_record_get_state(sr_p))); 1245 | if (ret_val) { 1246 | err_msg = "failed to serialize public key"; 1247 | goto cleanup; 1248 | } 1249 | 1250 | ret_val = 1; 1251 | *pubkey_data_pp = key_data_p; 1252 | 1253 | cleanup: 1254 | if (ret_val < 0) { 1255 | axc_log(ctx_p, AXC_LOG_ERROR, "%s: %s", __func__, err_msg); 1256 | axc_buf_free(key_data_p); 1257 | } 1258 | 1259 | SIGNAL_UNREF(sr_p); 1260 | SIGNAL_UNREF(pubkey_p); 1261 | 1262 | return ret_val; 1263 | } 1264 | -------------------------------------------------------------------------------- /libs/axc/axc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "signal_protocol.h" 6 | 7 | typedef struct axc_context axc_context; 8 | typedef struct axc_handshake axc_handshake; 9 | typedef struct axc_bundle axc_bundle; 10 | typedef struct axc_buf_list_item axc_buf_list_item; 11 | 12 | typedef signal_buffer axc_buf; 13 | typedef signal_protocol_address axc_address; 14 | 15 | #define AXC_LOG_ERROR 0 16 | #define AXC_LOG_WARNING 1 17 | #define AXC_LOG_NOTICE 2 18 | #define AXC_LOG_INFO 3 19 | #define AXC_LOG_DEBUG 4 20 | 21 | #define AXC_ERR -10000 22 | #define AXC_ERR_NOMEM -10001 23 | #define AXC_ERR_NOT_A_PREKEY_MSG -10100 24 | #define AXC_ERR_INVALID_KEY_ID -10200 25 | 26 | #define AXC_DB_DEFAULT_FN "axc.sqlite" 27 | #define AXC_PRE_KEYS_AMOUNT 100 28 | 29 | /** 30 | * Allocates the axc context. 31 | * 32 | * @param ctx_pp Will point to the created context. 33 | * @return 0 on success, negative on failure 34 | */ 35 | int axc_context_create(axc_context ** ctx_pp); 36 | 37 | /** 38 | * Sets the filename/location of the db. 39 | * Should be done after creating the context, but before calling axc_init(). 40 | * 41 | * @param ctx_p The fresh axc context. 42 | * @param filename The filename/path to be used. 43 | * @param fn_len Length of the filename. 44 | * @return 0 on success, negative on failure 45 | */ 46 | int axc_context_set_db_fn(axc_context * ctx_p, char * filename, size_t fn_len); 47 | 48 | /** 49 | * Returns the filename to be used for the database. 50 | * 51 | * @param ctx_p The axc context. 52 | * @return The filename set via axc_context_set_db_fn(), or AXC_DEFAULT_DB_FN if none was set. 53 | */ 54 | char * axc_context_get_db_fn(axc_context * ctx_p); 55 | 56 | void axc_context_set_log_func(axc_context * ctx_p, void (*log_func)(int level, const char * message, size_t len, void * user_data)); 57 | void axc_context_set_log_level(axc_context * ctx_p, int level); 58 | int axc_context_get_log_level(axc_context * ctx_p); 59 | 60 | void axc_context_destroy_all(axc_context * ctx_p); 61 | signal_context * axc_context_get_axolotl_ctx(axc_context * ctx_p); 62 | 63 | void axc_default_log(int level, const char *message, size_t len, void *user_data); 64 | void axc_log(axc_context * ctx_p, int level, const char * format, ...); 65 | 66 | int axc_buf_list_item_create(axc_buf_list_item ** item_pp, uint32_t * id_p, axc_buf * data_p); 67 | void axc_buf_list_item_set_next(axc_buf_list_item * item_p, axc_buf_list_item * next_p); 68 | axc_buf_list_item * axc_buf_list_item_get_next(axc_buf_list_item * item_p); 69 | uint32_t axc_buf_list_item_get_id(axc_buf_list_item * item_p); 70 | axc_buf * axc_buf_list_item_get_buf(axc_buf_list_item * item_p); 71 | void axc_buf_list_free(axc_buf_list_item * head_p); 72 | 73 | /** 74 | * Collects the info needed to publish a bundle. 75 | * 76 | * @param n Number of pre keys to get. 77 | * @param ctx_p Pointer to the initialized axc context. 78 | * @param bundle_pp Will be set to the bundle. 79 | * @return 0 on success, negative on error. 80 | */ 81 | int axc_bundle_collect(size_t n, axc_context * ctx_p, axc_bundle ** bundle_pp); 82 | uint32_t axc_bundle_get_reg_id(axc_bundle * bundle_p); 83 | axc_buf_list_item * axc_bundle_get_pre_key_list(axc_bundle * bundle_p); 84 | uint32_t axc_bundle_get_signed_pre_key_id(axc_bundle * bundle_p); 85 | axc_buf * axc_bundle_get_signed_pre_key(axc_bundle * bundle_p); 86 | axc_buf * axc_bundle_get_signature(axc_bundle * bundle_p); 87 | axc_buf * axc_bundle_get_identity_key(axc_bundle * bundle_p); 88 | void axc_bundle_destroy(axc_bundle * bundle_p); 89 | 90 | /** 91 | * Initializes the library. Has to be called at every startup. 92 | * 93 | * @param ctx_p A pointer to an already created axc context. 94 | * @return 0 on success, negative on failure 95 | */ 96 | int axc_init(axc_context * ctx_p); 97 | 98 | /** 99 | * Destroys mutexes and axolotl contexts saved in the axc context. 100 | * 101 | * @param ctx_p Pointer to the axc context as received from axc_init(). 102 | */ 103 | void axc_cleanup(axc_context * ctx_p); 104 | 105 | /** 106 | * "Installs" the library by creating the database and saving the necessary encryption keys into it. 107 | * Needs to be called once at the beginning, but can be called at every startup as it will not touch an initialized database. 108 | * 109 | * @param ctx_p Pointer to the axc context as received from axc_init(). 110 | * @return 0 on success, negative on failure 111 | */ 112 | int axc_install(axc_context * ctx_p); 113 | 114 | /** 115 | * Retrieves the local registration ID. 116 | * 117 | * @param ctx_p Pointer to an initialized and installed axc_context. 118 | * @param id_p Will be set to the ID. 119 | * @return 0 on success, negative on error. 120 | */ 121 | int axc_get_device_id(axc_context * ctx_p, uint32_t * id_p); 122 | 123 | axc_buf * axc_buf_create(const uint8_t * data, size_t len); 124 | uint8_t * axc_buf_get_data(axc_buf * buf); 125 | size_t axc_buf_get_len(axc_buf * buf); 126 | void axc_buf_free(axc_buf * buf); 127 | 128 | axc_buf * axc_handshake_get_data(axc_handshake * handshake_p); 129 | 130 | /** 131 | * Generates the message that is needed to initiate a session synchronously, which internally is axolotl's key_exchange_message. 132 | * The returned axc_handshake has to be kept and given to axc_handshake_acknowledge(), together with the received response. 133 | * At the end, it should be freed by calling axc_handshake_destroy(). 134 | * 135 | * The whole key exchange process looks like this: 136 | * A initiate 137 | * A -> B 138 | * B accept 139 | * B -> A 140 | * A acknowledge 141 | * 142 | * @param recipient_addr_p The address of the recipient. 143 | * @param ctx_p The client context. 144 | * @param handshake_pp The handshake struct to keep for the next steps. 145 | * @return 0 on success, negative on error 146 | */ 147 | int axc_handshake_initiate(axc_address * recipient_addr_p, axc_context * ctx_p, axc_handshake ** handshake_init_pp); 148 | 149 | /** 150 | * Second step of the session establishment, is called by the recipient. 151 | * 152 | * @param msg_data_p A pointer to a buffer with the raw message data. 153 | * @param sender_addr_p A pointer to an address struct with the sender's information. 154 | * @param ctx_p The axc context. 155 | * @param handshake_response_pp Will point to the response message if successful, unset otherwise. Has to be freed using axc_handshake_destroy(). 156 | * @return 0 on success, negative on error. 157 | */ 158 | int axc_handshake_accept(axc_buf * msg_data_p, axc_address * sender_addr_p, axc_context * ctx_p, axc_handshake ** handshake_response_pp); 159 | 160 | /** 161 | * Third and final step of session establishment. 162 | * 163 | * @param msg_data_p A pointer to a buffer containing the raw message data. 164 | * @param handshake_p Pointer to the axc_handshake returned by axc_handshake_initiate(). Should be freed by axc_handshake_destroy() afterwards. 165 | * @param ctx_p The axc context. 166 | * @return 0 on success, negative on error. 167 | * 168 | */ 169 | int axc_handshake_acknowledge(axc_buf * msg_data_p, axc_handshake * handshake_p, axc_context * ctx_p); 170 | 171 | /** 172 | * Frees the memory used by this struct and its members. 173 | * 174 | * @param The axc_handshake to destroy. 175 | */ 176 | void axc_handshake_destroy(axc_handshake * hs); 177 | 178 | /** 179 | * Encrypts a message. Needs an established session, either synchronous or built from bundle. 180 | * The buffer containing the ciphertext has to be freed afterwards. 181 | * 182 | * If data is a string, include the null terminator in the data. 183 | * 184 | * @param msg_p The data to encrypt. 185 | * @param recipient_addr_p Address of the recipient. 186 | * @param ctx_p The axc context. 187 | * @param ciphertext_pp Will point to the serialized ciphertext afterwards. 188 | * @return 0 on success, negative on error. 189 | */ 190 | int axc_message_encrypt_and_serialize(axc_buf * msg_p, const axc_address * recipient_addr_p, axc_context * ctx_p, axc_buf ** ciphertext_pp); 191 | 192 | /** 193 | * Decrypts a received message. Needs an established session. 194 | * 195 | * As the null terminator should be included in the data bytes to be encrypted in case of a string, 196 | * the data of the axc_buf should also work as a string after decryption. 197 | * 198 | * @param msg_p The data to decrypt. 199 | * @param sender_addr_p Address of the sender. 200 | * @param ctx_p The axc context. 201 | * @param plaintext_pp Will point to the plaintext afterwards. Has to be freed. 202 | * @return 0 on success, negative on error. 203 | */ 204 | int axc_message_decrypt_from_serialized (axc_buf * msg_p, axc_address * sender_addr_p, axc_context * ctx_p, axc_buf ** plaintext_pp); 205 | 206 | /** 207 | * Checks if an initiated session exists (and no pending synchronous handshake). 208 | * 209 | * @param addr_p The address for which to check if a session exists. 210 | * @param ctx_p The axc context. 211 | * @return 1 if it exists, 0 if it does not, negative on error 212 | */ 213 | int axc_session_exists_initiated(const axc_address * addr_p, axc_context * ctx_p); 214 | 215 | /** 216 | * Checks if there exists a session for a user. 217 | * 218 | * @param name The username. 219 | * @param ctx_p Pointer to the axc context. 220 | * @return 1 if at least one session exists, 0 if no session exists, negative on error. 221 | */ 222 | int axc_session_exists_any(const char * name, axc_context * ctx_p); 223 | 224 | /** 225 | * Creates a session from a fetched bundle which can then instantly be used to encrypt a message. 226 | * 227 | * @param pre_key_id The ID of the used prekey. 228 | * @param pre_key_public_serialized_p Pointer to a buffer containing the serialized public part of the pre key pair. 229 | * @param signed_pre_key_id The ID of the signed prekey. 230 | * @param signed_pre_key_public_serialized_p Pointer to a buffer containing the serialized public part of the signed pre key pair. 231 | * @param signed_pre_key_signature_p Pointer to a buffer containing the signature data of the signed pre key. 232 | * @param identity_key_public_serialized_p Pointer to a buffer containing the serialized public part of the identity key pair. 233 | * @param remote_address_p Pointer to the address of the recipient. 234 | * @param ctx_p Pointer to the axc_context. 235 | * @return 0 on success, negative on error. 236 | */ 237 | int axc_session_from_bundle(uint32_t pre_key_id, 238 | axc_buf * pre_key_public_serialized_p, 239 | uint32_t signed_pre_key_id, 240 | axc_buf * signed_pre_key_public_serialized_p, 241 | axc_buf * signed_pre_key_signature_p, 242 | axc_buf * identity_key_public_serialized_p, 243 | const axc_address * remote_address_p, 244 | axc_context * ctx_p); 245 | 246 | /** 247 | * Deletes a session for a user:device combination. 248 | * 249 | * @param user Username. 250 | * @param device_id The device ID. 251 | * @param ctx_p Pointer to the axc context. 252 | * @return 0 on success, negative on error. 253 | */ 254 | int axc_session_delete(const char * user, uint32_t device_id, axc_context * ctx_p); 255 | 256 | /** 257 | * Creates a session from a received pre key message and uses it to decrypt the actual message body.. 258 | * The ciphertext is decrypted here to avoid reserializing the message or having to deal with internal axolotl data structures. 259 | * 260 | * @param pre_key_msg_serialized_p Pointer to the buffer containing the serialized message. 261 | * @param remote_address_p Pointer to the remote (sender) address. 262 | * @param ctx_p Pointer to the axc context. 263 | * @param msg_pp Will contain a pointer to the decrypted plaintext. 264 | * @return 0 on success, negative on error 265 | */ 266 | int axc_pre_key_message_process(axc_buf * pre_key_msg_serialized_p, axc_address * remote_address_p, axc_context * ctx_p, axc_buf ** plaintext_pp); 267 | 268 | /** 269 | * Retrieves the own public identity key. 270 | * 271 | * @param ctx_p Pointer to the axc_context. 272 | * @param pubkey_data_pp Will point to an axc_buf * containing the serialized key data. 273 | * @return 0 on success, negative on error. 274 | */ 275 | int axc_key_load_public_own(axc_context * ctx_p, axc_buf ** pubkey_data_pp); 276 | 277 | /** 278 | * Retrieves the serialized public identity key for a user's device. 279 | * 280 | * @param name The user's name. 281 | * @param device_id The device's ID. 282 | * @param ctx_p Pointer to the axc_context. 283 | * @param pubkey_data_pp Will point to an axc_buf * which contains the data. 284 | * @return 1 if the key was loaded, 0 if no session exists, negative on error. 285 | */ 286 | int axc_key_load_public_addr(const char * name, uint32_t device_id, axc_context * ctx_p, axc_buf ** pubkey_data_pp); 287 | -------------------------------------------------------------------------------- /libs/axc/axc_crypto.c: -------------------------------------------------------------------------------- 1 | #include // int types 2 | #include // fprintf 3 | #include // malloc 4 | 5 | #include 6 | 7 | #include "signal_protocol.h" 8 | 9 | #include "axc.h" 10 | 11 | void axc_crypto_init(void) { 12 | (void) gcry_check_version((void *) 0); 13 | gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN); 14 | gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); 15 | gcry_control (GCRYCTL_RESUME_SECMEM_WARN); 16 | gcry_control(GCRYCTL_USE_SECURE_RNDPOOL); 17 | gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 18 | } 19 | 20 | void axc_crypto_teardown(void) { 21 | } 22 | 23 | int random_bytes(uint8_t * data_p, size_t len, void * user_data_p) { 24 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 25 | (void) axc_ctx_p; 26 | 27 | gcry_randomize(data_p, len, GCRY_STRONG_RANDOM); 28 | 29 | return SG_SUCCESS; 30 | } 31 | 32 | int hmac_sha256_init(void ** hmac_context_pp, const uint8_t * key_p, size_t key_len, void * user_data_p) { 33 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 34 | int ret_val = 0; 35 | char * err_msg = (void *) 0; 36 | 37 | gcry_mac_hd_t * hmac_hd_p = (void *) 0; 38 | 39 | hmac_hd_p = malloc(sizeof(gcry_mac_hd_t)); 40 | if (!hmac_hd_p) { 41 | err_msg = "could not malloc hmac-sha256 ctx"; 42 | ret_val = SG_ERR_NOMEM; 43 | goto cleanup; 44 | } 45 | 46 | ret_val = gcry_mac_open(hmac_hd_p, GCRY_MAC_HMAC_SHA256, 0, (void *) 0); 47 | if (ret_val) { 48 | err_msg = "could not create hmac-sha256 ctx"; 49 | goto cleanup; 50 | } 51 | 52 | ret_val = gcry_mac_setkey(*hmac_hd_p, key_p, key_len); 53 | if (ret_val) { 54 | err_msg = "could not set key for hmac"; 55 | goto cleanup; 56 | } 57 | 58 | *hmac_context_pp = hmac_hd_p; 59 | 60 | cleanup: 61 | if (ret_val) { 62 | if (ret_val > 0) { 63 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 64 | ret_val = SG_ERR_UNKNOWN; 65 | } else { 66 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 67 | } 68 | 69 | if (hmac_hd_p) { 70 | gcry_mac_close(*hmac_hd_p); 71 | free(hmac_hd_p); 72 | } 73 | } 74 | 75 | return ret_val; 76 | } 77 | 78 | int hmac_sha256_update(void * hmac_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p) { 79 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 80 | (void) axc_ctx_p; 81 | 82 | gcry_mac_write(*((gcry_mac_hd_t *) hmac_context_p), data_p, data_len); 83 | 84 | return SG_SUCCESS; 85 | } 86 | 87 | int hmac_sha256_final(void * hmac_context_p, signal_buffer ** output_pp, void * user_data_p) { 88 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 89 | int ret_val = 0; 90 | char * err_msg = (void *) 0; 91 | 92 | int algo = GCRY_MAC_HMAC_SHA256; 93 | size_t mac_len = 0; 94 | uint8_t * mac_data_p = (void *) 0; 95 | signal_buffer * out_buf_p = (void *) 0; 96 | 97 | mac_len = gcry_mac_get_algo_maclen(algo); 98 | 99 | mac_data_p = malloc(sizeof(uint8_t) * mac_len); 100 | if (!mac_data_p) { 101 | ret_val = SG_ERR_NOMEM; 102 | err_msg = "failed to malloc mac buf"; 103 | goto cleanup; 104 | } 105 | 106 | ret_val = gcry_mac_read(*((gcry_mac_hd_t *) hmac_context_p), mac_data_p, &mac_len); 107 | if (ret_val) { 108 | err_msg = "failed to read mac"; 109 | goto cleanup; 110 | } 111 | 112 | out_buf_p = signal_buffer_create(mac_data_p, mac_len); 113 | if (!out_buf_p) { 114 | ret_val = SG_ERR_NOMEM; 115 | err_msg = "failed to create mac output buf"; 116 | goto cleanup; 117 | } 118 | 119 | *output_pp = out_buf_p; 120 | 121 | cleanup: 122 | if (ret_val) { 123 | if (ret_val > 0) { 124 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 125 | ret_val = SG_ERR_UNKNOWN; 126 | } else { 127 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 128 | } 129 | } 130 | free(mac_data_p); 131 | 132 | return ret_val; 133 | } 134 | 135 | void hmac_sha256_cleanup(void * hmac_context_p, void * user_data_p) { 136 | (void) user_data_p; 137 | 138 | gcry_mac_hd_t * mac_hd_p = (gcry_mac_hd_t *) hmac_context_p; 139 | 140 | gcry_mac_close(*mac_hd_p); 141 | free(mac_hd_p); 142 | } 143 | 144 | int sha512_digest_init(void ** digest_context_pp, void * user_data_p) { 145 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 146 | int ret_val = 0; 147 | char * err_msg = (void *) 0; 148 | 149 | gcry_md_hd_t * hash_hd_p = (void *) 0; 150 | hash_hd_p = malloc(sizeof(gcry_mac_hd_t)); 151 | if (!hash_hd_p) { 152 | err_msg = "could not malloc sha512 ctx"; 153 | ret_val = SG_ERR_NOMEM; 154 | goto cleanup; 155 | } 156 | 157 | ret_val = gcry_md_open(hash_hd_p, GCRY_MD_SHA512, 0); 158 | if (ret_val) { 159 | err_msg = "could not create sha512 ctx"; 160 | goto cleanup; 161 | } 162 | 163 | *digest_context_pp = hash_hd_p; 164 | 165 | cleanup: 166 | if (ret_val) { 167 | if (ret_val > 0) { 168 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 169 | ret_val = SG_ERR_UNKNOWN; 170 | } else { 171 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 172 | } 173 | 174 | if (hash_hd_p) { 175 | gcry_md_close(*hash_hd_p); 176 | free(hash_hd_p); 177 | } 178 | } 179 | 180 | return ret_val; 181 | } 182 | 183 | int sha512_digest_update(void * digest_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p) { 184 | (void) user_data_p; 185 | 186 | gcry_md_write(*((gcry_md_hd_t *) digest_context_p), data_p, data_len); 187 | 188 | return 0; 189 | } 190 | 191 | int sha512_digest_final(void * digest_context_p, signal_buffer ** output_pp, void * user_data_p) { 192 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 193 | gcry_md_hd_t * hash_hd_p = (gcry_md_hd_t *) digest_context_p; 194 | int ret_val = 0; 195 | char * err_msg = (void *) 0; 196 | 197 | int algo = GCRY_MD_SHA512; 198 | size_t hash_len = 0; 199 | unsigned char * hash_data_p = (void *) 0; 200 | signal_buffer * out_buf_p = (void *) 0; 201 | 202 | hash_len = gcry_md_get_algo_dlen(algo); 203 | 204 | hash_data_p = gcry_md_read(*hash_hd_p, algo); 205 | if (!hash_data_p) { 206 | ret_val = SG_ERR_UNKNOWN; 207 | err_msg = "failed to read hash"; 208 | goto cleanup; 209 | } 210 | 211 | out_buf_p = signal_buffer_create((uint8_t *) hash_data_p, hash_len); 212 | if (!out_buf_p) { 213 | ret_val = SG_ERR_NOMEM; 214 | err_msg = "failed to create hash output buf"; 215 | goto cleanup; 216 | } 217 | 218 | gcry_md_reset(*hash_hd_p); 219 | 220 | *output_pp = out_buf_p; 221 | 222 | cleanup: 223 | if (ret_val) { 224 | if (ret_val > 0) { 225 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 226 | ret_val = SG_ERR_UNKNOWN; 227 | } else { 228 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 229 | } 230 | } 231 | 232 | return ret_val; 233 | } 234 | 235 | void sha512_digest_cleanup(void * digest_context_p, void * user_data_p) { 236 | (void) user_data_p; 237 | 238 | gcry_md_hd_t * hash_hd_p = (gcry_md_hd_t *) digest_context_p; 239 | 240 | gcry_md_close(*hash_hd_p); 241 | free(hash_hd_p); 242 | } 243 | 244 | static int choose_aes(int cipher, size_t key_len, int * algo_p, int * mode_p) { 245 | int algo = 0; 246 | int mode = 0; 247 | 248 | switch(key_len) { 249 | case 16: 250 | algo = GCRY_CIPHER_AES128; 251 | break; 252 | case 24: 253 | algo = GCRY_CIPHER_AES192; 254 | break; 255 | case 32: 256 | algo = GCRY_CIPHER_AES256; 257 | break; 258 | default: 259 | return SG_ERR_UNKNOWN; 260 | } 261 | 262 | switch (cipher) { 263 | case SG_CIPHER_AES_CBC_PKCS5: 264 | mode = GCRY_CIPHER_MODE_CBC; 265 | break; 266 | case SG_CIPHER_AES_CTR_NOPADDING: 267 | mode = GCRY_CIPHER_MODE_CTR; 268 | break; 269 | default: 270 | return SG_ERR_UNKNOWN; 271 | } 272 | 273 | *algo_p = algo; 274 | *mode_p = mode; 275 | 276 | return 0; 277 | } 278 | 279 | int aes_encrypt(signal_buffer ** output_pp, 280 | int cipher, 281 | const uint8_t * key_p, size_t key_len, 282 | const uint8_t * iv_p, size_t iv_len, 283 | const uint8_t * plaintext_p, size_t plaintext_len, 284 | void * user_data_p) { 285 | 286 | int ret_val = SG_SUCCESS; 287 | char * err_msg = (void *) 0; 288 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 289 | 290 | int algo = 0; 291 | int mode = 0; 292 | size_t pad_len = 0; 293 | size_t ct_len = 0; 294 | gcry_cipher_hd_t cipher_hd = {0}; 295 | uint8_t * pt_p = (void *) 0; 296 | uint8_t * out_p = (void *) 0; 297 | signal_buffer * out_buf_p = (void *) 0; 298 | 299 | if(iv_len != 16) { 300 | err_msg = "invalid AES IV size (must be 16)"; 301 | ret_val = SG_ERR_UNKNOWN; 302 | goto cleanup; 303 | } 304 | 305 | ret_val = choose_aes(cipher, key_len, &algo, &mode); 306 | if (ret_val) { 307 | err_msg = "failed to choose cipher"; 308 | ret_val = SG_ERR_UNKNOWN; 309 | goto cleanup; 310 | } 311 | 312 | ret_val = gcry_cipher_open(&cipher_hd, algo, mode, 0); 313 | if (ret_val) { 314 | err_msg = "failed to init cipher"; 315 | goto cleanup; 316 | } 317 | 318 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 319 | if (ret_val) { 320 | err_msg = "failed to set key"; 321 | goto cleanup; 322 | } 323 | 324 | switch (cipher) { 325 | case SG_CIPHER_AES_CBC_PKCS5: 326 | pad_len = 16 - (plaintext_len % 16); 327 | if (pad_len == 0) { 328 | pad_len = 16; 329 | } 330 | ct_len = plaintext_len + pad_len; 331 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 332 | if (ret_val) { 333 | err_msg = "failed to set iv"; 334 | goto cleanup; 335 | } 336 | break; 337 | case SG_CIPHER_AES_CTR_NOPADDING: 338 | ct_len = plaintext_len; 339 | ret_val = gcry_cipher_setctr(cipher_hd, iv_p, iv_len); 340 | if (ret_val) { 341 | err_msg = "failed to set iv"; 342 | goto cleanup; 343 | } 344 | break; 345 | default: 346 | ret_val = SG_ERR_UNKNOWN; 347 | err_msg = "unknown cipher"; 348 | goto cleanup; 349 | } 350 | 351 | pt_p = malloc(sizeof(uint8_t) * ct_len); 352 | if (!pt_p) { 353 | err_msg = "failed to malloc pt buf"; 354 | ret_val = SG_ERR_NOMEM; 355 | goto cleanup; 356 | } 357 | memset(pt_p, pad_len, ct_len); 358 | memcpy(pt_p, plaintext_p, plaintext_len); 359 | 360 | out_p = malloc(sizeof(uint8_t) * ct_len); 361 | if (!out_p) { 362 | err_msg = "failed to malloc ct buf"; 363 | ret_val = SG_ERR_NOMEM; 364 | goto cleanup; 365 | } 366 | 367 | ret_val = gcry_cipher_encrypt(cipher_hd, out_p, ct_len, pt_p, ct_len); 368 | if (ret_val) { 369 | err_msg = "failed to encrypt"; 370 | goto cleanup; 371 | } 372 | 373 | out_buf_p = signal_buffer_create(out_p, ct_len); 374 | *output_pp = out_buf_p; 375 | 376 | cleanup: 377 | if (ret_val) { 378 | if (ret_val > 0) { 379 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 380 | ret_val = SG_ERR_UNKNOWN; 381 | } else { 382 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 383 | } 384 | } 385 | 386 | free(out_p); 387 | gcry_cipher_close(cipher_hd); 388 | 389 | return ret_val; 390 | } 391 | 392 | int aes_decrypt(signal_buffer ** output_pp, 393 | int cipher, 394 | const uint8_t * key_p, size_t key_len, 395 | const uint8_t * iv_p, size_t iv_len, 396 | const uint8_t * ciphertext_p, size_t ciphertext_len, 397 | void * user_data_p) { 398 | 399 | int ret_val = SG_SUCCESS; 400 | char * err_msg = (void *) 0; 401 | axc_context * axc_ctx_p = (axc_context *) user_data_p; 402 | 403 | int algo = 0; 404 | int mode = 0; 405 | gcry_cipher_hd_t cipher_hd = {0}; 406 | uint8_t * out_p = (void *) 0; 407 | size_t pad_len = 0; 408 | signal_buffer * out_buf_p = (void *) 0; 409 | 410 | if(iv_len != 16) { 411 | err_msg = "invalid AES IV size (must be 16)"; 412 | ret_val = SG_ERR_UNKNOWN; 413 | goto cleanup; 414 | } 415 | 416 | ret_val = choose_aes(cipher, key_len, &algo, &mode); 417 | if (ret_val) { 418 | err_msg = "failed to choose cipher"; 419 | ret_val = SG_ERR_UNKNOWN; 420 | goto cleanup; 421 | } 422 | 423 | ret_val = gcry_cipher_open(&cipher_hd, algo, mode, 0); 424 | if (ret_val) { 425 | err_msg = "failed to init cipher"; 426 | goto cleanup; 427 | } 428 | 429 | ret_val = gcry_cipher_setkey(cipher_hd, key_p, key_len); 430 | if (ret_val) { 431 | err_msg = "failed to set key"; 432 | goto cleanup; 433 | } 434 | 435 | switch (cipher) { 436 | case SG_CIPHER_AES_CBC_PKCS5: 437 | pad_len = 1; 438 | ret_val = gcry_cipher_setiv(cipher_hd, iv_p, iv_len); 439 | if (ret_val) { 440 | err_msg = "failed to set iv"; 441 | goto cleanup; 442 | } 443 | break; 444 | case SG_CIPHER_AES_CTR_NOPADDING: 445 | ret_val = gcry_cipher_setctr(cipher_hd, iv_p, iv_len); 446 | if (ret_val) { 447 | err_msg = "failed to set iv"; 448 | goto cleanup; 449 | } 450 | break; 451 | default: 452 | ret_val = SG_ERR_UNKNOWN; 453 | err_msg = "unknown cipher"; 454 | goto cleanup; 455 | } 456 | 457 | out_p = malloc(sizeof(uint8_t) * ciphertext_len); 458 | if (!out_p) { 459 | err_msg = "failed to malloc pt buf"; 460 | ret_val = SG_ERR_NOMEM; 461 | goto cleanup; 462 | } 463 | 464 | ret_val = gcry_cipher_decrypt(cipher_hd, out_p, ciphertext_len, ciphertext_p, ciphertext_len); 465 | if (ret_val) { 466 | err_msg = "failed to decrypt"; 467 | goto cleanup; 468 | } 469 | 470 | if (pad_len) { 471 | pad_len = out_p[ciphertext_len - 1]; 472 | } 473 | 474 | out_buf_p = signal_buffer_create(out_p, ciphertext_len - pad_len); 475 | *output_pp = out_buf_p; 476 | 477 | 478 | cleanup: 479 | if (ret_val) { 480 | if (ret_val > 0) { 481 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s (%s: %s)\n", __func__, err_msg, gcry_strsource(ret_val), gcry_strerror(ret_val)); 482 | ret_val = SG_ERR_UNKNOWN; 483 | } else { 484 | axc_log(axc_ctx_p, AXC_LOG_ERROR, "%s: %s\n", __func__, err_msg); 485 | } 486 | } 487 | 488 | free(out_p); 489 | gcry_cipher_close(cipher_hd); 490 | 491 | return ret_val; 492 | } 493 | -------------------------------------------------------------------------------- /libs/axc/axc_crypto.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "signal_protocol.h" 6 | 7 | void axc_crypto_init(void); 8 | void axc_crypto_teardown(void); 9 | 10 | int random_bytes(uint8_t * data_p, size_t len, void * user_data_p); 11 | 12 | int hmac_sha256_init(void ** hmac_context_pp, const uint8_t * key_p, size_t key_len, void * user_data_p); 13 | int hmac_sha256_update(void * hmac_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p); 14 | int hmac_sha256_final(void * hmac_context_p, signal_buffer ** output_pp, void * user_data_p); 15 | void hmac_sha256_cleanup(void * hmac_context_p, void * user_data_p); 16 | 17 | int sha512_digest_init(void ** digest_context_pp, void * user_data_p); 18 | int sha512_digest_update(void * digest_context_p, const uint8_t * data_p, size_t data_len, void * user_data_p); 19 | int sha512_digest_final(void * digest_context_p, signal_buffer ** output_pp, void * user_data_p); 20 | void sha512_digest_cleanup(void * digest_context_p, void * user_data_p); 21 | 22 | int aes_encrypt(signal_buffer ** output_pp, 23 | int cipher, 24 | const uint8_t * key_p, size_t key_len, 25 | const uint8_t * iv_p, size_t iv_len, 26 | const uint8_t * plaintext_p, size_t plaintext_len, 27 | void * user_data_p); 28 | int aes_decrypt(signal_buffer ** output_pp, 29 | int cipher, 30 | const uint8_t * key_p, size_t key_len, 31 | const uint8_t * iv_p, size_t iv_len, 32 | const uint8_t * ciphertext_p, size_t ciphertext_len, 33 | void * user_data_p); 34 | -------------------------------------------------------------------------------- /libs/axc/axc_crypto.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loguntsov/erl_signal/651bf66293e12b8cf66a29d959db8d565fa675c0/libs/axc/axc_crypto.o -------------------------------------------------------------------------------- /libs/axc/axc_store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "signal_protocol.h" 4 | #include "key_helper.h" 5 | 6 | #include "axc.h" 7 | 8 | // For docs see axolotl.h 9 | 10 | #define AXC_DB_NOT_INITIALIZED (-1) 11 | #define AXC_DB_NEEDS_ROLLBACK 0 12 | #define AXC_DB_INITIALIZED 1 13 | 14 | // session store 15 | int axc_db_session_load(signal_buffer **record, const signal_protocol_address *address, void *user_data); 16 | int axc_db_session_get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data); 17 | int axc_db_session_store(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data); 18 | int axc_db_session_contains(const signal_protocol_address *address, void *user_data); 19 | int axc_db_session_delete(const signal_protocol_address *address, void *user_data); 20 | int axc_db_session_delete_all(const char *name, size_t name_len, void *user_data); 21 | void axc_db_session_destroy_store_ctx(void *user_data); 22 | 23 | // pre key store 24 | int axc_db_pre_key_load(signal_buffer **record, uint32_t pre_key_id, void *user_data); 25 | int axc_db_pre_key_store(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data); 26 | int axc_db_pre_key_contains(uint32_t pre_key_id, void *user_data); 27 | int axc_db_pre_key_remove(uint32_t pre_key_id, void *user_data); 28 | void axc_db_pre_key_destroy_ctx(void *user_data); 29 | /** 30 | * Stores a whole list of pre keys at once, inside a single transaction. 31 | * 32 | * @param pre_keys_head Pointer to the first element of the list. 33 | * @param user_data_p Optional. The user_data as received from the axolotl interface, will be used to set the database name. 34 | */ 35 | int axc_db_pre_key_store_list(signal_protocol_key_helper_pre_key_list_node * pre_keys_head, axc_context * ctx_p); 36 | 37 | /** 38 | * Gets the specified number of pre keys for publishing, i.e. only their public part. 39 | * 40 | * @param amount Number of keys to retrieve. 41 | * @param ctx_p Pointer to the initialized axc context. 42 | * @param list_head_pp Will be set to the head of the list. 43 | * @return 0 on success, negative on error. 44 | */ 45 | int axc_db_pre_key_get_list(size_t amount, axc_context * ctx_p, axc_buf_list_item ** list_head_pp); 46 | 47 | /** 48 | * Retrieves the highest existing pre key ID that is not the last resort key's ID. 49 | * 50 | * @param ctx_p Pointer to the axc context. 51 | * @param max_id_p Will be set to the highest ID that is not MAX_INT. 52 | * @return 0 on success, negative on error. 53 | */ 54 | int axc_db_pre_key_get_max_id(axc_context * ctx_p, uint32_t * max_id_p); 55 | 56 | /** 57 | * Returns the count of pre keys saved in the database. 58 | * This includes the "last resort" key that is additionally generated at db init. 59 | * 60 | * @param ctx_p Pointer to the axc context. 61 | * @param count_p Will point to the number of pre keys. 62 | * @return 0 on success, negative on error. 63 | */ 64 | int axc_db_pre_key_get_count(axc_context * ctx_p, size_t * count_p); 65 | 66 | // signed pre key store 67 | int axc_db_signed_pre_key_load(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data); 68 | int axc_db_signed_pre_key_store(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data); 69 | int axc_db_signed_pre_key_contains(uint32_t signed_pre_key_id, void *user_data); 70 | int axc_db_signed_pre_key_remove(uint32_t signed_pre_key_id, void *user_data); 71 | void axc_db_signed_pre_key_destroy_ctx(void *user_data); 72 | 73 | // identity key store 74 | int axc_db_identity_get_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data); 75 | int axc_db_identity_get_local_registration_id(void *user_data, uint32_t *registration_id); 76 | int axc_db_identity_save(const signal_protocol_address * addr_p, uint8_t *key_data, size_t key_len, void *user_data); 77 | int axc_db_identity_is_trusted(const char *name, size_t name_len, uint8_t *key_data, size_t key_len, void *user_data); 78 | int axc_db_identity_always_trusted(const signal_protocol_address * addr_p, uint8_t * key_data, size_t key_len, void * user_data); 79 | void axc_db_identity_destroy_ctx(void *user_data); 80 | 81 | // additional helper functions 82 | /** 83 | * Saves the public and private key by using the api serialization calls, as this format (and not the higher-level key type) is needed by the getter. 84 | * 85 | * @param Pointer to the keypair as returned by axolotl_key_helper_generate_identity_key_pair 86 | * @param axc_ctx_p Pointer to the axc context. 87 | * @return 0 on success, negative on error 88 | */ 89 | int axc_db_identity_set_key_pair(const ratchet_identity_key_pair * key_pair_p, axc_context * axc_ctx_p); 90 | 91 | /** 92 | * Saves the axolotl registration ID which was obtained by a call to axolotl_key_helper_generate_registration_id(). 93 | * 94 | * @param reg_id The ID. 95 | * @param axc_ctx_p Pointer to the axc context. 96 | * @return 0 on success, negative on error 97 | */ 98 | int axc_db_identity_set_local_registration_id(const uint32_t reg_id, axc_context * axc_ctx_p); 99 | 100 | // other 101 | /** 102 | * Creates the necessary tables. Safe to call if they already exist. 103 | * 104 | * @param axc_ctx_p Pointer to the axc context. 105 | * @return 0 on success, negative on error 106 | */ 107 | int axc_db_create(axc_context * axc_ctx_p); 108 | 109 | /** 110 | * Drops all the tables so that the db can be reset. 111 | * 112 | * @param axc_ctx_p Pointer to the axc context. 113 | * @return 0 on success, negative on error 114 | */ 115 | int axc_db_destroy(axc_context * axc_ctx_p); 116 | 117 | 118 | /** 119 | * Sets the value of a property in the database's "settings" table. 120 | * 121 | * @param name The name of the property. 122 | * @param status The int value of the property. 123 | * @param axc_ctx_p Pointer to the axc context. 124 | * @return 0 on success, negative on error 125 | */ 126 | int axc_db_property_set(const char * name, const int val, axc_context * axc_ctx_p); 127 | 128 | /** 129 | * Gets a property from the settings table. 130 | * 131 | * @param name Name of the property 132 | * @param val_p Pointer to where the saved value should be stored. 133 | * @param axc_ctx_p Pointer to the axc context. 134 | * @return 0 on success, negative on error, 1 if no sql error but no result 135 | */ 136 | int axc_db_property_get(const char * name, int * val_p, axc_context * axc_ctx_p); 137 | 138 | /** 139 | * "Partial application" of db_set_property, setting the init status value. 140 | * 141 | * @param status AXC_DB_NOT INITIALIZED, AXC_DB_NEEDS_ROOLBACK, or AXC_DB_INITIALIZED 142 | * @param axc_ctx_p Pointer to the axc context. 143 | * @return 0 on success, negative on error 144 | */ 145 | int axc_db_init_status_set(const int status, axc_context * axc_ctx_p); 146 | 147 | /** 148 | * "Partial application" of db_get_property, getting the init status value. 149 | * 150 | * @param init_status_p The value behind this pointer will be set to the init status number. 151 | * @param axc_ctx_p Pointer to the axc context. 152 | * @return 0 on success, negative on error, 1 if no sql error but no result 153 | */ 154 | int axc_db_init_status_get(int * init_status_p, axc_context * axc_ctx_p); 155 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Erlang NIF: Binding for libsignal-protocol-c (ALPHA) 2 | 3 | ## Requirements 4 | 5 | sudo apt-get install libgcrypt20-dev gcc-multilib g++-multilib libglib2.0-dev cmake 6 | 7 | ## BUILD 8 | 9 | To build it you should place rebar3 to root of this project. 10 | 11 | ## USAGE 12 | 13 | make all -- compile 14 | 15 | make run -- compile and run in shell. 16 | 17 | make test -- run tests 18 | 19 | -------------------------------------------------- 20 | 21 | You can find examples of using in tests. 22 | 23 | ## Thanks 24 | 25 | This C-code based on AXC-library: https://github.com/gkdr/axc 26 | 27 | ## License 28 | 29 | LGPL3 30 | 31 | 32 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, []}. 3 | 4 | {pre_hooks, [ 5 | {"(linux|darwin|solaris)", compile, "./scripts/compile.sh" } 6 | ]}. 7 | 8 | {post_hooks, [ 9 | {"(linux|darwin|solaris)", clean, "make -C c_src clean"} 10 | ]}. 11 | 12 | {shell, [ 13 | { apps, [ erl_signal, sasl ]} 14 | ]}. 15 | 16 | {ct_opts, [ 17 | {logdir, "test/ct_logs"}, 18 | %% {config, ["ct.config"]}, 19 | %%{sys_config, "sys.config"}, 20 | {verbose, false} 21 | ]}. -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /scripts/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | make -C ./libs 3 | make -C ./c_src -j 5 4 | -------------------------------------------------------------------------------- /src/erl_signal.app.src: -------------------------------------------------------------------------------- 1 | {application, erl_signal, [ 2 | {description, "Erlang binding for libsignal-c"}, 3 | {vsn, "0.1"}, 4 | {registered, []}, 5 | {applications, [ 6 | kernel, 7 | stdlib 8 | ]}, 9 | {env, []} 10 | ]}. 11 | -------------------------------------------------------------------------------- /src/erl_signal.erl: -------------------------------------------------------------------------------- 1 | -module(erl_signal). 2 | 3 | -include("erl_signal.hrl"). 4 | 5 | -export([ 6 | new/0, 7 | generate_identity_keys/1, 8 | is_session_exists_initiated/2, 9 | handshake_initiate/3, 10 | handshake_accept/3, 11 | handshake_acknowledge/3, 12 | encode/3, decode/3 13 | ]). 14 | 15 | new() -> 16 | erl_signal_nif:new(). 17 | 18 | generate_identity_keys(Session) -> 19 | erl_signal_nif:generate_identity_keys(Session). 20 | 21 | is_session_exists_initiated(Session, MyAddress) when ?IS_ES_ADDRESS(MyAddress) -> 22 | erl_signal_nif:is_session_exists_initiated(Session, MyAddress). 23 | 24 | handshake_initiate(Session, FromAddress, ToAddress) when ?IS_ES_ADDRESS(ToAddress) -> 25 | erl_signal_nif:handshake_initiate(Session, FromAddress, ToAddress). 26 | 27 | handshake_accept(Session, FromAddress, Binary) when is_binary(Binary) -> 28 | erl_signal_nif:handshake_accept(Session, FromAddress, Binary). 29 | 30 | handshake_acknowledge(Session, SessionBuilder, Binary) when is_binary(Binary) -> 31 | erl_signal_nif:handshake_acknowledge(Session, SessionBuilder, Binary). 32 | 33 | encode(Session, ToAddress, Binary) when ?IS_ES_ADDRESS(ToAddress), is_binary(Binary) -> 34 | erl_signal_nif:encode(Session, ToAddress, Binary). 35 | 36 | decode(Session, FromAddress, Binary) when ?IS_ES_ADDRESS(FromAddress), is_binary(Binary) -> 37 | erl_signal_nif:decode(Session, FromAddress, Binary). 38 | -------------------------------------------------------------------------------- /src/erl_signal_nif.erl: -------------------------------------------------------------------------------- 1 | -module(erl_signal_nif). 2 | 3 | -export([ 4 | new/0, 5 | generate_identity_keys/1, 6 | is_session_exists_initiated/2, 7 | handshake_initiate/3, 8 | handshake_accept/3, 9 | handshake_acknowledge/3, 10 | encode/3, decode/3, 11 | serialize/1 12 | ]). 13 | 14 | -on_load(init/0). 15 | 16 | -include("erl_signal.hrl"). 17 | 18 | -type address() :: #es_address{}. 19 | -type session() :: reference() | binary(). %% Depends from version of Erlang 20 | -type session_builder() :: reference() | binary(). %% Depends from version of Erlang 21 | -type session_cipher() :: reference() | binary(). %% Depends from version of Erlang 22 | 23 | -export_type([ 24 | address/0, session/0, session_builder/0, session_cipher/0 25 | ]). 26 | 27 | init() -> 28 | SoName = case code:priv_dir(erl_signal) of 29 | {error, bad_name} -> 30 | case code:which(?MODULE) of 31 | Filename when is_list(Filename) -> 32 | filename:join([filename:dirname(Filename),"../priv", "erl_signal_nif"]); 33 | _ -> 34 | filename:join("../priv", "erl_signal_nif") 35 | end; 36 | Dir -> 37 | filename:join(Dir, "erl_signal_nif") 38 | end, 39 | erlang:load_nif(SoName, 0). 40 | 41 | -spec new() -> { ok, Session :: session() }. 42 | new() -> 43 | erlang:nif_error({error, not_loaded}). 44 | 45 | -spec generate_identity_keys(session()) -> ok. 46 | generate_identity_keys(_Session) -> 47 | erlang:nif_error({error, not_loaded}). 48 | 49 | -spec is_session_exists_initiated(session(), address()) -> boolean(). 50 | is_session_exists_initiated(_Session, _MyAddress) -> 51 | erlang:nif_error({error, not_loaded}). 52 | 53 | -spec handshake_initiate(session(), address(), address()) -> { ok, session_cipher(), session_builder(), Response :: binary() } | { error, Reason :: atom() }. 54 | handshake_initiate(_Session, _FromAddress, _ToAddress) -> 55 | erlang:nif_error({error, not_loaded}). 56 | 57 | -spec handshake_accept(session(), address(), binary()) -> { ok, session_cipher(), session_builder(), Response :: binary() } | { error, Reason :: atom() }. 58 | handshake_accept(_Session, _FromAddress, _Handshake) -> 59 | erlang:nif_error({error, not_loaded}). 60 | 61 | -spec handshake_acknowledge(session(), session_builder(), binary()) -> {ok, From :: address() } | { error, Reason :: atom() }. 62 | handshake_acknowledge(_Session, _SessionAddress, _Binary) -> 63 | erlang:nif_error({error, not_loaded}). 64 | 65 | -spec encode(session(), address(), binary()) -> { ok, binary()} | { error, Reason :: atom() }. 66 | encode(_Session, _ToAddress, _Binary) -> 67 | erlang:nif_error({error, not_loaded}). 68 | 69 | -spec decode(session(), address(), binary()) -> {ok, binary()} | { error, Reason :: atom() }. 70 | decode(_Session, _FromAddress, _Binary) -> 71 | erlang:nif_error({error, not_loaded}). 72 | 73 | serialize(_Session) -> 74 | erlang:nif_error({error, not_loaded}). 75 | 76 | -------------------------------------------------------------------------------- /test/cpp/Makefile: -------------------------------------------------------------------------------- 1 | # Based on c_src.mk from erlang.mk by Loic Hoguin 2 | 3 | PROJECT := tera_crypto 4 | 5 | CURDIR := $(shell pwd) 6 | BASEDIR := $(abspath $(CURDIR)/..) 7 | 8 | PROJECT ?= $(notdir $(BASEDIR)) 9 | PROJECT := $(strip $(PROJECT)) 10 | 11 | ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)]).") 12 | ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]).") 13 | ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]).") 14 | 15 | C_SRC_DIR = $(CURDIR) 16 | C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so 17 | 18 | # System type and C compiler/flags. 19 | 20 | UNAME_SYS := $(shell uname -s) 21 | ifeq ($(UNAME_SYS), Darwin) 22 | CC ?= cc 23 | CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes 24 | CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall 25 | LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress 26 | else ifeq ($(UNAME_SYS), FreeBSD) 27 | CC ?= cc 28 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 29 | CXXFLAGS ?= -O3 -finline-functions -Wall 30 | else ifeq ($(UNAME_SYS), Linux) 31 | CC ?= gcc 32 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 33 | CXXFLAGS ?= -O3 -finline-functions -Wall 34 | endif 35 | 36 | CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) 37 | CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) 38 | 39 | LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei 40 | LDFLAGS += -shared 41 | 42 | # Verbosity. 43 | 44 | c_verbose_0 = @echo " C " $(?F); 45 | c_verbose = $(c_verbose_$(V)) 46 | 47 | cpp_verbose_0 = @echo " CPP " $(?F); 48 | cpp_verbose = $(cpp_verbose_$(V)) 49 | 50 | link_verbose_0 = @echo " LD " $(@F); 51 | link_verbose = $(link_verbose_$(V)) 52 | 53 | SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) 54 | OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) 55 | 56 | COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c 57 | COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c 58 | 59 | $(C_SRC_OUTPUT): $(OBJECTS) 60 | @mkdir -p $(BASEDIR)/priv/ 61 | $(link_verbose) g++ $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) 62 | 63 | %.o: %.c 64 | $(COMPILE_C) $(OUTPUT_OPTION) $< 65 | 66 | %.o: %.cc 67 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 68 | 69 | %.o: %.C 70 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 71 | 72 | %.o: %.cpp 73 | $(COMPILE_CPP) $(OUTPUT_OPTION) $< 74 | 75 | clean: 76 | @rm -f $(C_SRC_OUTPUT) $(OBJECTS) 77 | -------------------------------------------------------------------------------- /test/main_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(main_SUITE). 2 | 3 | -include_lib("common_test/include/ct.hrl"). 4 | -include_lib("eunit/include/eunit.hrl"). 5 | 6 | -include("erl_signal.hrl"). 7 | 8 | %% API 9 | -compile(export_all). 10 | 11 | init_per_suite(Config) -> 12 | {ok, _} = application:ensure_all_started(erl_signal), 13 | Config. 14 | 15 | end_per_suite(Config) -> 16 | application:stop(erl_signal), 17 | Config. 18 | 19 | init_per_testcase(_, Config) -> 20 | Config. 21 | 22 | end_per_testcase(_TestCaseName, _Config) -> 23 | _Config. 24 | 25 | all() -> [ 26 | main_test, 27 | bad_handshake_accept_test, 28 | initiate_without_acknowledge 29 | ]. 30 | 31 | main_test(_) -> 32 | { ok, Alice } = erl_signal_nif:new(), 33 | ok = erl_signal_nif:generate_identity_keys(Alice), 34 | AliceAddress = #es_address{ 35 | name = <<"alice">>, 36 | device_id = 5 37 | }, 38 | 39 | { ok, Bob } = erl_signal_nif:new(), 40 | ok = erl_signal_nif:generate_identity_keys(Bob), 41 | BobAddress = #es_address{ 42 | name = <<"bob">>, 43 | device_id = 2 44 | }, 45 | 46 | false = erl_signal_nif:is_session_exists_initiated(Alice, BobAddress), 47 | false = erl_signal_nif:is_session_exists_initiated(Bob, AliceAddress), 48 | 49 | { ok, AliceHandshake } = erl_signal_nif:handshake_initiate(Alice, AliceAddress, BobAddress), 50 | true = is_binary(AliceHandshake), 51 | 52 | { ok, AliceAddress, BobHandshake } = erl_signal_nif:handshake_accept(Bob, BobAddress, binary:copy(AliceHandshake)), 53 | 54 | true = (BobHandshake /= AliceHandshake), 55 | true = is_binary(BobHandshake), 56 | 57 | true = erl_signal_nif:is_session_exists_initiated(Bob, AliceAddress), 58 | 59 | { ok, BobAddress } = erl_signal_nif:handshake_acknowledge(Alice, AliceAddress, BobHandshake), 60 | true = erl_signal_nif:is_session_exists_initiated(Alice, BobAddress), 61 | 62 | Bin2 = <<"Hello from Bob">>, 63 | { ok, EncryptedHello2 } = erl_signal_nif:encode(Bob, AliceAddress, Bin2), 64 | { ok, Bin2} = erl_signal_nif:decode(Alice, BobAddress, EncryptedHello2), 65 | 66 | Bin1 = <<"Hello from Bob with love">>, 67 | { ok, EncryptedHello3 } = erl_signal_nif:encode(Bob, AliceAddress, Bin1), 68 | { ok, Bin1} = erl_signal_nif:decode(Alice, BobAddress, EncryptedHello3), 69 | 70 | Bin3 = <<"Hello from Alice with love">>, 71 | { ok, EncryptedHello1 } = erl_signal_nif:encode(Alice, BobAddress, Bin3), 72 | { ok, Bin3 } = erl_signal_nif:decode(Bob, AliceAddress, EncryptedHello1 ), 73 | 74 | ok. 75 | 76 | bad_handshake_accept_test(_) -> 77 | { ok, Alice } = erl_signal_nif:new(), 78 | ok = erl_signal_nif:generate_identity_keys(Alice), 79 | AliceAddress = #es_address{ 80 | name = <<"alice">>, 81 | device_id = 5 82 | }, 83 | 84 | {error,bad_handshake} = erl_signal_nif:handshake_accept(Alice, AliceAddress, <<"this is bad handshake">>), 85 | {error,bad_handshake} = erl_signal_nif:handshake_accept(Alice, AliceAddress, <<"">>), 86 | 87 | ok. 88 | 89 | initiate_without_acknowledge(_) -> 90 | { ok, Alice } = erl_signal_nif:new(), 91 | ok = erl_signal_nif:generate_identity_keys(Alice), 92 | AliceAddress = #es_address{ 93 | name = <<"alice">>, 94 | device_id = 5 95 | }, 96 | 97 | { ok, Bob } = erl_signal_nif:new(), 98 | ok = erl_signal_nif:generate_identity_keys(Bob), 99 | BobAddress = #es_address{ 100 | name = <<"bob">>, 101 | device_id = 2 102 | }, 103 | 104 | { ok, AliceHandshake } = erl_signal_nif:handshake_initiate(Alice, AliceAddress, BobAddress), 105 | { ok, AliceAddress, _BobHandshake } = erl_signal_nif:handshake_accept(Bob, BobAddress, binary:copy(AliceHandshake)), 106 | 107 | true = erl_signal_nif:is_session_exists_initiated(Bob, AliceAddress), 108 | 109 | Bin2 = <<"Hello from Bob">>, 110 | { ok, EncryptedHello2 } = erl_signal_nif:encode(Bob, AliceAddress, Bin2), 111 | { ok, Bin2 } = erl_signal_nif:decode(Alice, BobAddress, EncryptedHello2), 112 | 113 | true = erl_signal_nif:is_session_exists_initiated(Alice, BobAddress), 114 | 115 | true = erl_signal_nif:is_session_exists_initiated(Alice, BobAddress), 116 | 117 | ok. 118 | 119 | --------------------------------------------------------------------------------