├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── crypto
├── crypto_gcrypt.c
├── crypto_provider.h
└── targets.cmake
├── profanity
└── targets.cmake
├── store
├── omemo_file_store.c
├── omemo_store.h
└── targets.cmake
├── structs
├── device_list.c
├── device_list.h
├── omemo_context.c
├── omemo_context.h
├── omemo_device.c
├── omemo_device.h
└── targets.cmake
├── test
├── CMakeLists.txt
├── test_main.h
├── test_omemo_crypto.cpp
├── test_omemo_device.cpp
├── test_omemo_device_list.cpp
├── test_omemo_pubsub.cpp
└── test_omemo_store.cpp
└── xmpp
├── omemo_constants.h
├── pubsub.c
├── pubsub.h
└── targets.cmake
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 | *.ko
4 | *.obj
5 | *.elf
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Libraries
12 | *.lib
13 | *.a
14 | *.la
15 | *.lo
16 |
17 | # Shared objects (inc. Windows DLLs)
18 | *.dll
19 | *.so
20 | *.so.*
21 | *.dylib
22 |
23 | # Executables
24 | *.exe
25 | *.out
26 | *.app
27 | *.i*86
28 | *.x86_64
29 | *.hex
30 |
31 | # Debug files
32 | *.dSYM/
33 | *.su
34 |
35 | # global Build directory
36 | build/*
37 | lib/*
38 | test/ext/*
39 | ext/*
40 |
41 | # VIM workspace
42 | *.swp
43 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required (VERSION 2.7)
2 |
3 | project (profanity-omemo)
4 |
5 | ###############################################################################
6 | # for testing
7 | find_package (Threads REQUIRED)
8 | include (CTest)
9 | include (ExternalProject)
10 |
11 | ExternalProject_Add (
12 | gtest
13 | URL https://github.com/google/googletest/archive/master.zip
14 | PREFIX ${PROJECT_SOURCE_DIR}/test/ext
15 | INSTALL_COMMAND ""
16 | )
17 | # Get GTest informations
18 | ExternalProject_Get_Property (gtest source_dir binary_dir)
19 |
20 | add_library (libgtest IMPORTED STATIC GLOBAL)
21 | add_dependencies (libgtest gtest)
22 |
23 | # Set libgtest properties
24 | set_target_properties(libgtest PROPERTIES
25 | "IMPORTED_LOCATION" "${binary_dir}/googlemock/gtest/libgtest.a"
26 | "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}"
27 | )
28 | include_directories("${source_dir}/googletest/include"
29 | "${source_dir}/googlemock/include")
30 |
31 | ###############################################################################
32 |
33 | set (LIBSIGNAL_DIR "${PROJECT_SOURCE_DIR}/ext/libsignal")
34 | set (LIBSIGNAL_INCLUDE_DIR "${LIBSIGNAL_DIR}/src/libsignal/src")
35 |
36 | ExternalProject_Add (
37 | libsignal
38 | GIT_REPOSITORY "https://github.com/WhisperSystems/libsignal-protocol-c.git"
39 | GIT_TAG "master"
40 | PREFIX ${LIBSIGNAL_DIR}
41 | SOURCE_DIR ""
42 | BINARY_DIR libsignal
43 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug -D CMAKE_C_FLAGS=-fPIC -Wswitch -Wunused-variable -Wunused-value -Wshadow -Wint-conversion -Wpointer-sign -Wprotocol -Wshorten-64-to-32
44 | INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/libsignal/src/libsignal-protocol-c.a ${LIBSIGNAL_DIR}/
45 | )
46 |
47 | include_directories (${LIBSIGNAL_INCLUDE_DIR})
48 |
49 | set (LIBSIGNAL ${LIBSIGNAL_DIR}/libsignal-protocol-c.a)
50 | find_library (LIBGCRYPT libgcrypt.so)
51 | find_package (LibXml2 REQUIRED)
52 |
53 | include_directories (${LIBXML2_INCLUDE_DIR})
54 |
55 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/lib/DEBUG)
56 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/lib/DEBUG)
57 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
58 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
59 |
60 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -std=c99 -fPIC")
61 |
62 | include_directories (${PROJECT_SOURCE_DIR})
63 |
64 | include ("${PROJECT_SOURCE_DIR}/crypto/targets.cmake")
65 | include ("${PROJECT_SOURCE_DIR}/profanity/targets.cmake")
66 | include ("${PROJECT_SOURCE_DIR}/store/targets.cmake")
67 | include ("${PROJECT_SOURCE_DIR}/structs/targets.cmake")
68 | include ("${PROJECT_SOURCE_DIR}/xmpp/targets.cmake")
69 |
70 | add_library (
71 | profanity-omemo SHARED
72 | ${CRYPTO_SRC}
73 | ${PROF_OMEMO_SRC}
74 | ${STORE_SRC}
75 | ${STRUCTS_SRC}
76 | ${XMPP_SRC}
77 | )
78 |
79 | add_dependencies (profanity-omemo libsignal)
80 |
81 | target_link_libraries (profanity-omemo "${LIBSIGNAL}" "${LIBXML2_LIBRARIES}"
82 | "${LIBGCRYPT}")
83 |
84 | add_subdirectory ("${PROJECT_SOURCE_DIR}/test")
85 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU 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 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # profanity-omemo
2 |
3 | ## Build
4 | The following dependencies are needed to build the plugin:
5 | * `cmake`
6 | * `libxml2`
7 | * `libgcrypt`
8 | * `libsignal-protocol-c.a`
9 |
10 | `libsignal-protocol-c.a` is linked to the plugin statically so it is not a runtime dependency and can be deleted after building
11 | `profanity-omemo` whereas `libxml2` and `libgcrypt` is needed at runtime. The signal library will be built and linked with the
12 | OMEMO plugin automatically and will not install anything to your system.
13 |
14 | The plugin can be built with the following steps:
15 | ```
16 | $ cd profanity-omemo
17 | $ mkdir build
18 | $ cd build
19 | $ cmake (for debug purposes: -DCMAKE_BUILD_TYPE=Debug; enable testing: -DBUILD_TESTING=ON) ..
20 | $ make
21 | ```
22 | You can run the (for now minimal) test suite:
23 | ```
24 | $ make test
25 | ```
26 | which will show a summary of all run tests. You can also run a single test binary for more detailed informations.
27 |
--------------------------------------------------------------------------------
/crypto/crypto_gcrypt.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "crypto_provider.h"
5 |
6 |
7 | int omemo_init_provider()
8 | {
9 | if (!gcry_check_version(NULL)) {
10 | fputs("Wrong libgcrypt version!", stderr);
11 | return -1;
12 | }
13 |
14 | gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
15 |
16 | return 0;
17 | }
18 |
19 | int omemo_random_numbers(uint8_t *data, size_t len, void *user_data)
20 | {
21 | if (!data) {
22 | return SG_ERR_INVAL;
23 | }
24 |
25 | gcry_randomize(data, len, GCRY_STRONG_RANDOM);
26 |
27 | return 0;
28 | }
29 |
30 | int omemo_hmac_sha256_init(void **hmac_context, const uint8_t *key,
31 | size_t data_len, void *user_data)
32 | {
33 | if (!hmac_context || !key) {
34 | return SG_ERR_INVAL;
35 | }
36 |
37 | UNUSED(user_data);
38 |
39 | gcry_error_t err = GPG_ERR_NO_ERROR;
40 | gcry_mac_hd_t *hd = NULL;
41 |
42 | hd = malloc(sizeof(gcry_mac_hd_t));
43 | if (!hd) {
44 | return SG_ERR_NOMEM;
45 | }
46 |
47 | err = gcry_mac_open(hd, GCRY_MAC_HMAC_SHA256, 0, NULL);
48 | if (err) {
49 | free(hd);
50 | return SG_ERR_UNKNOWN;
51 | }
52 |
53 | err = gcry_mac_setkey(*hd, key, data_len);
54 | if (err) {
55 | free(hd);
56 | return SG_ERR_UNKNOWN;
57 | }
58 |
59 | *hmac_context = hd;
60 |
61 | return 0;
62 | }
63 |
64 | int omemo_hmac_sha256_update(void *hmac_context, const uint8_t *data,
65 | size_t data_len, void *user_data)
66 | {
67 | if (!hmac_context || !data) {
68 | return SG_ERR_INVAL;
69 | }
70 |
71 | UNUSED(user_data);
72 |
73 | gcry_error_t err = GPG_ERR_NO_ERROR;
74 | gcry_mac_hd_t *hd = hmac_context;
75 |
76 | err = gcry_mac_write(*hd, data, data_len);
77 | if (err) {
78 | free(hd);
79 | return SG_ERR_UNKNOWN;
80 | }
81 |
82 | return 0;
83 | }
84 |
85 | int omemo_hmac_sha256_final(void *hmac_context, signal_buffer **output,
86 | void *user_data)
87 | {
88 | UNUSED(user_data);
89 |
90 | if (!hmac_context || !output) {
91 | return SG_ERR_INVAL;
92 | }
93 |
94 | gcry_error_t err = GPG_ERR_NO_ERROR;
95 | gcry_mac_hd_t *hd = hmac_context;
96 | signal_buffer *sig_buffer = NULL;
97 | size_t mac_len;
98 |
99 | mac_len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256);
100 | uint8_t buffer[mac_len];
101 |
102 | err = gcry_mac_read(*hd, buffer, &mac_len);
103 | if (err) {
104 | free(hd);
105 | return SG_ERR_UNKNOWN;
106 | }
107 |
108 | sig_buffer = signal_buffer_create(buffer, mac_len);
109 | if (!sig_buffer) {
110 | free(hd);
111 | return SG_ERR_UNKNOWN;
112 | }
113 |
114 | *output = sig_buffer;
115 |
116 | return 0;
117 | }
118 |
119 | void omemo_hmac_sha256_cleanup(void *hmac_context, void *user_data)
120 | {
121 | UNUSED(user_data);
122 |
123 | gcry_mac_hd_t *hd = hmac_context;
124 |
125 | if (!hmac_context) {
126 | return;
127 | }
128 |
129 | gcry_mac_close(*hd);
130 | free(hmac_context);
131 | }
132 |
133 | int omemo_sha512_digest_init(void **digest_context, void *user_data)
134 | {
135 | UNUSED(user_data);
136 |
137 | if (!digest_context) {
138 | return SG_ERR_INVAL;
139 | }
140 |
141 | gcry_error_t err = GPG_ERR_NO_ERROR;
142 | gcry_md_hd_t *hd = NULL;
143 |
144 | hd = malloc(sizeof(gcry_md_hd_t));
145 | if (!hd) {
146 | return SG_ERR_NOMEM;
147 | }
148 |
149 | err = gcry_md_open(hd, GCRY_MD_SHA512, 0);
150 | if (err < 0) {
151 | return SG_ERR_UNKNOWN;
152 | }
153 |
154 | *digest_context = hd;
155 |
156 | return 0;
157 | }
158 |
159 | int omemo_sha512_digest_update(void *digest_context, const uint8_t *data,
160 | size_t data_len, void *user_data)
161 | {
162 | UNUSED(user_data);
163 |
164 | if (!digest_context || !data) {
165 | puts("digest update error");
166 | return SG_ERR_INVAL;
167 | }
168 |
169 | gcry_md_hd_t *hd = digest_context;
170 |
171 | gcry_md_write(*hd, data, data_len);
172 |
173 | return 0;
174 | }
175 |
176 | int omemo_sha512_digest_final(void *digest_context, signal_buffer **output,
177 | void *user_data)
178 | {
179 | UNUSED(user_data);
180 |
181 | if (!digest_context || !output) {
182 | return SG_ERR_INVAL;
183 | }
184 |
185 | uint8_t *buffer = NULL;
186 | gcry_md_hd_t *hd = digest_context;
187 | signal_buffer *out = NULL;
188 | size_t len;
189 |
190 | len = gcry_md_get_algo_dlen(GCRY_MD_SHA512);
191 |
192 | buffer = gcry_md_read(*hd, GCRY_MD_SHA512);
193 | if (!buffer) {
194 | puts("digest final error");
195 | return SG_ERR_UNKNOWN;
196 | }
197 |
198 | out = signal_buffer_create(buffer, len);
199 | if (!out) {
200 | puts("digest final error");
201 | return SG_ERR_NOMEM;
202 | }
203 |
204 | *output = out;
205 |
206 | return 0;
207 | }
208 |
209 | void omemo_sha512_digest_cleanup(void *digest_context, void *user_data)
210 | {
211 | UNUSED(user_data);
212 |
213 | if (!digest_context) {
214 | puts("digest cleanup error");
215 | return;
216 | }
217 |
218 | gcry_md_hd_t *hd = digest_context;
219 |
220 | gcry_md_close(*hd);
221 | free(digest_context);
222 | }
223 |
224 | static int omemo_endecrypt_aes_ctr(signal_buffer **output, const uint8_t *key,
225 | size_t key_len, const uint8_t *iv,
226 | size_t iv_len, const uint8_t *text,
227 | size_t text_len, int mode)
228 | {
229 | int cipher_mode = GCRY_CIPHER_MODE_CTR;
230 | int cipher_algo;
231 | gcry_cipher_hd_t handle;
232 | uint8_t *buffer;
233 | gcry_error_t err = GPG_ERR_NO_ERROR;
234 | signal_buffer *output_buffer;
235 |
236 | switch (key_len) {
237 | case 16:
238 | cipher_algo = GCRY_CIPHER_AES128;
239 | break;
240 | case 24:
241 | cipher_algo = GCRY_CIPHER_AES192;
242 | break;
243 | case 32:
244 | cipher_algo = GCRY_CIPHER_AES256;
245 | break;
246 | default:
247 | puts("unknown cipher length");
248 | return SG_ERR_UNKNOWN;
249 | }
250 |
251 | err = gcry_cipher_open(&handle, cipher_algo, cipher_mode, 0);
252 | if (err) {
253 | puts("cipher open error");
254 | return SG_ERR_UNKNOWN;
255 | }
256 |
257 | err = gcry_cipher_setkey(handle, key, key_len);
258 | if (err) {
259 | puts("cipher set key error");
260 | gcry_cipher_close(handle);
261 | return SG_ERR_UNKNOWN;
262 | }
263 |
264 | err = gcry_cipher_setiv(handle, iv, iv_len);
265 | if (err) {
266 | puts("cipher set iv error");
267 | gcry_cipher_close(handle);
268 | return SG_ERR_UNKNOWN;
269 | }
270 |
271 | buffer = malloc(text_len);
272 | if (!buffer) {
273 | gcry_cipher_close(handle);
274 | return SG_ERR_NOMEM;
275 | }
276 |
277 | if (mode == AES_CTR_ENCRYPT) {
278 | err = gcry_cipher_encrypt(handle, buffer, text_len, text, text_len);
279 | } else {
280 | err = gcry_cipher_decrypt(handle, buffer, text_len, text, text_len);
281 | }
282 | if (err) {
283 | puts("encrypt/decrypt error");
284 | gcry_cipher_close(handle);
285 | free(buffer);
286 | return SG_ERR_UNKNOWN;
287 | }
288 |
289 | output_buffer = signal_buffer_create(buffer, text_len);
290 | if (!output_buffer) {
291 | gcry_cipher_close(handle);
292 | free(buffer);
293 | return SG_ERR_NOMEM;
294 | }
295 |
296 | gcry_cipher_close(handle);
297 | free(buffer);
298 |
299 | *output = output_buffer;
300 |
301 | return 0;
302 | }
303 |
304 | static int omemo_encrypt_aes_cbc(signal_buffer **output,
305 | const uint8_t *key, size_t key_len,
306 | const uint8_t *iv, size_t iv_len,
307 | const uint8_t *plaintext, size_t plaintext_len)
308 | {
309 | int cipher_mode = GCRY_CIPHER_MODE_CBC;
310 | int cipher_algo;
311 | gcry_cipher_hd_t handle;
312 | gcry_error_t err = GPG_ERR_NO_ERROR;
313 | uint8_t *buffer;
314 | uint8_t *plaintext_padded;
315 | size_t block_size;
316 | size_t padding_len;
317 | size_t plaintext_padded_len;
318 | signal_buffer *output_buffer;
319 |
320 | switch (key_len) {
321 | case 16:
322 | cipher_algo = GCRY_CIPHER_AES128;
323 | break;
324 | case 32:
325 | cipher_algo = GCRY_CIPHER_AES192;
326 | break;
327 | case 64:
328 | cipher_algo = GCRY_CIPHER_AES256;
329 | break;
330 | default:
331 | puts("unknown cipher length");
332 | return SG_ERR_UNKNOWN;
333 | }
334 |
335 | block_size = gcry_cipher_get_algo_blklen(cipher_algo);
336 | if (!block_size) {
337 | puts("cipher block length error");
338 | return SG_ERR_UNKNOWN;
339 | }
340 |
341 | err = gcry_cipher_open(&handle, cipher_algo, cipher_mode, 0);
342 | if (err) {
343 | puts("cipher open error");
344 | return SG_ERR_UNKNOWN;
345 | }
346 |
347 | err = gcry_cipher_setkey(handle, key, key_len);
348 | if (err) {
349 | puts("cipher set key error");
350 | gcry_cipher_close(handle);
351 | return SG_ERR_UNKNOWN;
352 | }
353 |
354 | err = gcry_cipher_setiv(handle, iv, iv_len);
355 | if (err) {
356 | puts("cipher set iv error");
357 | gcry_cipher_close(handle);
358 | return SG_ERR_UNKNOWN;
359 | }
360 |
361 | padding_len = block_size - (plaintext_len % block_size);
362 | plaintext_padded_len = plaintext_len + padding_len;
363 | plaintext_padded = malloc(plaintext_padded_len);
364 | if (!plaintext_padded) {
365 | return SG_ERR_NOMEM;
366 | }
367 |
368 | memset(plaintext_padded, (uint8_t) padding_len, plaintext_padded_len);
369 | memcpy(plaintext_padded, plaintext, plaintext_len);
370 |
371 | buffer = malloc(plaintext_padded_len);
372 | if (!buffer) {
373 | free(plaintext_padded);
374 | gcry_cipher_close(handle);
375 | return SG_ERR_NOMEM;
376 | }
377 |
378 | err = gcry_cipher_encrypt(handle, buffer, plaintext_padded_len, plaintext_padded,
379 | plaintext_padded_len);
380 | if (err) {
381 | puts("cipher encrypt error");
382 | gcry_cipher_close(handle);
383 | free(plaintext_padded);
384 | free(buffer);
385 | return SG_ERR_UNKNOWN;
386 | }
387 |
388 | output_buffer = signal_buffer_create(buffer, plaintext_padded_len);
389 | if (!output_buffer) {
390 | gcry_cipher_close(handle);
391 | free(plaintext_padded);
392 | free(buffer);
393 | return SG_ERR_NOMEM;
394 | }
395 |
396 | *output = output_buffer;
397 |
398 | gcry_cipher_close(handle);
399 | free(plaintext_padded);
400 | free(buffer);
401 |
402 | return 0;
403 | }
404 |
405 | int omemo_encrypt(signal_buffer **output, int cipher,
406 | const uint8_t *key, size_t key_len,
407 | const uint8_t *iv, size_t iv_len,
408 | const uint8_t *plaintext, size_t plaintext_len,
409 | void *user_data)
410 | {
411 | UNUSED(user_data);
412 |
413 | if (!output || !key || !iv || !plaintext) {
414 | return SG_ERR_UNKNOWN;
415 | }
416 |
417 | if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
418 | return omemo_endecrypt_aes_ctr(output, key, key_len, iv, iv_len, plaintext, plaintext_len, AES_CTR_ENCRYPT);
419 | } else if (cipher == SG_CIPHER_AES_CBC_PKCS5) {
420 | return omemo_encrypt_aes_cbc(output, key, key_len, iv, iv_len, plaintext, plaintext_len);
421 | } else {
422 | return SG_ERR_UNKNOWN;
423 | }
424 | }
425 |
426 | static int omemo_decrypt_aes_cbc(signal_buffer **output, const uint8_t *key,
427 | size_t key_len, const uint8_t *iv,
428 | size_t iv_len, const uint8_t *ciphertext,
429 | size_t ciphertext_len)
430 | {
431 | int cipher_mode = GCRY_CIPHER_MODE_CBC;
432 | int cipher_algo;
433 | gcry_cipher_hd_t handle;
434 | gcry_error_t err = GPG_ERR_NO_ERROR;
435 | uint8_t *buffer;
436 | signal_buffer *output_buffer;
437 | size_t padding_len;
438 | size_t plaintext_unpadded_len;
439 |
440 | switch (key_len) {
441 | case 16:
442 | cipher_algo = GCRY_CIPHER_AES128;
443 | break;
444 | case 32:
445 | cipher_algo = GCRY_CIPHER_AES192;
446 | break;
447 | case 64:
448 | cipher_algo = GCRY_CIPHER_AES256;
449 | break;
450 | default:
451 | puts("unknown cipher length");
452 | return SG_ERR_UNKNOWN;
453 | }
454 |
455 | err = gcry_cipher_open(&handle, cipher_algo, cipher_mode, 0);
456 | if (err) {
457 | puts("cipher open error");
458 | return SG_ERR_UNKNOWN;
459 | }
460 |
461 | err = gcry_cipher_setkey(handle, key, key_len);
462 | if (err) {
463 | puts("cipher set key error");
464 | gcry_cipher_close(handle);
465 | return SG_ERR_UNKNOWN;
466 | }
467 |
468 | err = gcry_cipher_setiv(handle, iv, iv_len);
469 | if (err) {
470 | puts("cipher set iv error");
471 | gcry_cipher_close(handle);
472 | return SG_ERR_UNKNOWN;
473 | }
474 |
475 | buffer = malloc(ciphertext_len);
476 | if (!buffer) {
477 | gcry_cipher_close(handle);
478 | return SG_ERR_NOMEM;
479 | }
480 |
481 | err = gcry_cipher_decrypt(handle, buffer, ciphertext_len, ciphertext, ciphertext_len);
482 | if (err) {
483 | puts("cipher encrypt error");
484 | gcry_cipher_close(handle);
485 | free(buffer);
486 | return SG_ERR_UNKNOWN;
487 | }
488 |
489 | padding_len = buffer[ciphertext_len-1];
490 | plaintext_unpadded_len = ciphertext_len - padding_len;
491 |
492 | output_buffer = signal_buffer_create(buffer, plaintext_unpadded_len);
493 | if (!output_buffer) {
494 | gcry_cipher_close(handle);
495 | free(buffer);
496 | return SG_ERR_NOMEM;
497 | }
498 |
499 | *output = output_buffer;
500 |
501 | gcry_cipher_close(handle);
502 | free(buffer);
503 |
504 | return 0;
505 | }
506 |
507 | int omemo_decrypt(signal_buffer **output, int cipher,
508 | const uint8_t *key, size_t key_len,
509 | const uint8_t *iv, size_t iv_len,
510 | const uint8_t *ciphertext, size_t ciphertext_len,
511 | void *user_data)
512 | {
513 | UNUSED(user_data);
514 |
515 | if (!output || !key || !iv || !ciphertext) {
516 | return SG_ERR_UNKNOWN;
517 | }
518 |
519 | if (cipher == SG_CIPHER_AES_CTR_NOPADDING) {
520 | return omemo_endecrypt_aes_ctr(output, key, key_len, iv, iv_len, ciphertext, ciphertext_len, AES_CTR_DECRYPT);
521 | } else if (cipher == SG_CIPHER_AES_CBC_PKCS5) {
522 | return omemo_decrypt_aes_cbc(output, key, key_len, iv, iv_len, ciphertext, ciphertext_len);
523 | } else {
524 | return SG_ERR_UNKNOWN;
525 | }
526 | }
527 |
528 |
529 | signal_crypto_provider omemo_crypto_provider = {
530 | .random_func = omemo_random_numbers,
531 | .hmac_sha256_init_func = omemo_hmac_sha256_init,
532 | .hmac_sha256_update_func = omemo_hmac_sha256_update,
533 | .hmac_sha256_final_func = omemo_hmac_sha256_final,
534 | .hmac_sha256_cleanup_func = omemo_hmac_sha256_cleanup,
535 | .sha512_digest_init_func = omemo_sha512_digest_init,
536 | .sha512_digest_update_func = omemo_sha512_digest_update,
537 | .sha512_digest_final_func = omemo_sha512_digest_final,
538 | .sha512_digest_cleanup_func = omemo_sha512_digest_cleanup
539 | };
540 |
--------------------------------------------------------------------------------
/crypto/crypto_provider.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_CRYPTO_PROVIDER_H
2 | #define PROF_OMEMO_CRYPTO_PROVIDER_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | #define AES_CTR_ENCRYPT 1
11 | #define AES_CTR_DECRYPT 0
12 |
13 | #define UNUSED(x) (void)(x)
14 |
15 | extern signal_crypto_provider omemo_crypto_provider;
16 |
17 | int omemo_init_provider(void);
18 |
19 | int omemo_random_numbers(uint8_t *data, size_t len, void *user_data);
20 |
21 | int omemo_hmac_sha256_init(void **hmac_context, const uint8_t *key,
22 | size_t key_len, void *user_data);
23 |
24 | int omemo_hmac_sha256_update(void *hmac_context, const uint8_t *data,
25 | size_t data_len, void *user_data);
26 |
27 | int omemo_hmac_sha256_final(void *hmac_context, signal_buffer **output,
28 | void *user_data);
29 |
30 | void omemo_hmac_sha256_cleanup(void *hmac_context, void *user_data);
31 |
32 | int omemo_sha512_digest_init(void **digest_context, void *user_data);
33 |
34 | int omemo_sha512_digest_update(void *digest_context, const uint8_t *data,
35 | size_t data_len, void *user_data);
36 |
37 | int omemo_sha512_digest_final(void *digest_context, signal_buffer **output,
38 | void *user_data);
39 |
40 | void omemo_sha512_digest_cleanup(void *digest_context, void *user_data);
41 |
42 |
43 | int omemo_encrypt(signal_buffer **output, int cipher,
44 | const uint8_t *key, size_t key_len,
45 | const uint8_t *iv, size_t iv_len,
46 | const uint8_t *plaintext, size_t plaintext_len,
47 | void *user_data);
48 |
49 | int omemo_decrypt(signal_buffer **output, int cipher,
50 | const uint8_t *key, size_t key_len,
51 | const uint8_t *iv, size_t iv_len,
52 | const uint8_t *ciphertext, size_t ciphertext_len,
53 | void *user_data);
54 |
55 |
56 | #ifdef __cplusplus
57 | }
58 | #endif
59 |
60 | #endif /* PROF_OMEMO_CRYPTO_PROVIDER_H */
61 |
--------------------------------------------------------------------------------
/crypto/targets.cmake:
--------------------------------------------------------------------------------
1 | set (
2 | CRYPTO_SRC
3 | crypto/crypto_provider.h
4 | crypto/crypto_gcrypt.c
5 | )
6 |
--------------------------------------------------------------------------------
/profanity/targets.cmake:
--------------------------------------------------------------------------------
1 | set (PROF_OMEMO_SRC
2 | )
3 |
--------------------------------------------------------------------------------
/store/omemo_file_store.c:
--------------------------------------------------------------------------------
1 | #if _POSIX_C_SOURCE <= 200809L || !defined( _POSIX_C_SOURCE )
2 | #define _POSIX_C_SOURCE 200809L
3 | #endif
4 |
5 | #if _XOPEN_SOURCE <= 500 || !defined( _XOPEN_SOURCE )
6 | #define _XOPEN_SOURCE 500
7 | #endif
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | static const char se_file_name[] = "session";
27 | static const char dev_file_name[] = "devices";
28 | static const char pre_key_folder_name[] = "pre_keys";
29 | static const char signed_pre_key_folder_name[] = "signed_pre_keys";
30 | static const char id_pub_key_file_name[] = "id_key.pub";
31 | static const char id_priv_key_file_name[] = "id_key.priv";
32 |
33 | struct omemo_store_context *omemo_store_context_create(struct omemo_context *context)
34 | {
35 | struct omemo_store_context *store_context;
36 | const static struct omemo_store_context store_compound_init = {
37 | .session_store = {
38 | .load_session_func = omemo_load_session,
39 | .get_sub_device_sessions_func = omemo_get_sub_device_sessions,
40 | .store_session_func = omemo_store_session,
41 | .contains_session_func = omemo_contains_session,
42 | .delete_session_func = omemo_delete_session,
43 | .delete_all_sessions_func = omemo_delete_all_sessions,
44 | .destroy_func = omemo_session_store_destroy,
45 | },
46 | .pre_key_store = {
47 | .load_pre_key = omemo_load_pre_key,
48 | .store_pre_key = omemo_store_pre_key,
49 | .contains_pre_key = omemo_contains_pre_key,
50 | .remove_pre_key = omemo_remove_pre_key,
51 | .destroy_func = omemo_pre_key_store_destroy,
52 | },
53 | .signed_pre_key_store = {
54 | .load_signed_pre_key = omemo_load_signed_pre_key,
55 | .store_signed_pre_key = omemo_store_signed_pre_key,
56 | .contains_signed_pre_key = omemo_contains_signed_pre_key,
57 | .remove_signed_pre_key = omemo_remove_signed_pre_key,
58 | .destroy_func = omemo_signed_pre_key_store_destroy,
59 | },
60 | .identity_key_store = {
61 | .get_identity_key_pair = omemo_get_identity_key_pair,
62 | .get_local_registration_id = omemo_get_local_registration_id,
63 | .save_identity = omemo_save_identity,
64 | .is_trusted_identity = omemo_is_trusted_identity,
65 | .destroy_func = omemo_identity_key_store_destroy,
66 | }
67 | };
68 |
69 | if (!context) {
70 | errno = EINVAL;
71 | return NULL;
72 | }
73 |
74 | store_context = malloc(sizeof(struct omemo_store_context));
75 | if (!store_context) {
76 | return NULL;
77 | }
78 |
79 | *store_context = store_compound_init;
80 |
81 | store_context->session_store.user_data = context;
82 | store_context->pre_key_store.user_data = context;
83 | store_context->signed_pre_key_store.user_data = context;
84 | store_context->identity_key_store.user_data = context;
85 |
86 | return store_context;
87 | }
88 |
89 |
90 | static int omemo_mk_dir(const char *path)
91 | {
92 | char path_buf[PATH_MAX];
93 | char *p = NULL;
94 | size_t len;
95 |
96 | len = snprintf(path_buf, sizeof(path_buf), "%s", path);
97 | if (path_buf[len - 1] == '/') {
98 | path_buf[len - 1] = 0;
99 | } else {
100 | *strrchr(path_buf, '/') = 0;
101 | }
102 |
103 | for(p = path_buf + 1; *p; ++p) {
104 | if (*p == '/') {
105 | *p = 0;
106 | if (mkdir(path_buf, S_IRWXU) != 0) {
107 | if (errno != EEXIST) {
108 | return -1;
109 | }
110 | }
111 |
112 | *p = '/';
113 | }
114 | }
115 | if (mkdir(path_buf, S_IRWXU) != 0) {
116 | if (errno != EEXIST) {
117 | return -1;
118 | }
119 | }
120 |
121 | return 0;
122 | }
123 |
124 | /**
125 | * @brief Get the path to the pre key store to buffer
126 | *
127 | * @param local_user Pointer to the address of the local user
128 | * @param buffer Pointer to an allocated buffer
129 | * @param buf_len Length of the buffer
130 | *
131 | * @retval 0 The path to pre key store was found and written to the
132 | * buffer
133 | * @retval 1 The path to pre key store was not found but written to
134 | * buffer
135 | * @retval -1 An error occurred and `errno` is set to indicate the
136 | * cause
137 | */
138 | static int omemo_get_pre_key_store(const signal_protocol_address *local_user,
139 | char *buffer, size_t buf_len)
140 | {
141 | size_t bytes_written;
142 | struct stat st = {0};
143 |
144 | if (!local_user || !buffer) {
145 | errno = EINVAL;
146 | return -1;
147 | }
148 |
149 | bytes_written = snprintf(buffer, buf_len, "%s/%s", getenv("HOME"),
150 | OMEMO_WORKING_DIR);
151 |
152 | bytes_written += snprintf(buffer + bytes_written, local_user->name_len + 2,
153 | "/%s", local_user->name);
154 |
155 | bytes_written += snprintf(buffer + bytes_written,
156 | buf_len - bytes_written,
157 | "/%d/%s/", local_user->device_id,
158 | pre_key_folder_name);
159 |
160 | if (bytes_written > buf_len) {
161 | errno = ENOBUFS;
162 | return -1;
163 | }
164 |
165 | return (stat(buffer, &st) < 0) ? 1 : 0;
166 | }
167 |
168 | /**
169 | * @brief Get the path to the signed pre key store to buffer
170 | *
171 | * @param local_user Pointer to the address of the local user
172 | * @param buffer Pointer to an allocated buffer
173 | * @param buf_len Length of the buffer
174 | *
175 | * @retval 0 The path to signed pre key store was found and written
176 | * to the buffer
177 | * @retval 1 The path to signed pre key store was not found but
178 | * written to buffer
179 | * @retval -1 An error occurred and `errno` is set to indicate the
180 | * cause
181 | */
182 | static int
183 | omemo_get_signed_pre_key_store(const signal_protocol_address *local_user,
184 | char *buffer, size_t buf_len)
185 | {
186 | size_t bytes_written;
187 | struct stat st = {0};
188 |
189 | if (!local_user || !buffer) {
190 | errno = EINVAL;
191 | return -1;
192 | }
193 |
194 | bytes_written = snprintf(buffer, buf_len, "%s/%s", getenv("HOME"),
195 | OMEMO_WORKING_DIR);
196 |
197 | bytes_written += snprintf(buffer + bytes_written, local_user->name_len + 2,
198 | "/%s", local_user->name);
199 |
200 | bytes_written += snprintf(buffer + bytes_written,
201 | buf_len - bytes_written,
202 | "/%d/%s/", local_user->device_id,
203 | signed_pre_key_folder_name);
204 |
205 | if (bytes_written > buf_len) {
206 | errno = ENOBUFS;
207 | return -1;
208 | }
209 |
210 | return (stat(buffer, &st) < 0) ? 1 : 0;
211 | }
212 |
213 | static int omemo_get_identity_key_store(const signal_protocol_address *local_user,
214 | char *buffer, size_t buf_len)
215 | {
216 | size_t bytes_written;
217 | struct stat st = {0};
218 |
219 | if (!local_user || !buffer) {
220 | errno = EINVAL;
221 | return -1;
222 | }
223 |
224 | bytes_written = snprintf(buffer, buf_len, "%s/%s", getenv("HOME"),
225 | OMEMO_WORKING_DIR);
226 |
227 | bytes_written += snprintf(buffer + bytes_written, local_user->name_len + 2,
228 | "/%s", local_user->name);
229 |
230 | bytes_written += snprintf(buffer + bytes_written, buf_len - bytes_written,
231 | "%d/", local_user->device_id);
232 |
233 | if (bytes_written > buf_len) {
234 | errno = ENOBUFS;
235 | return -1;
236 | }
237 |
238 | return (stat(buffer, &st) < 0) ? 1: 0;
239 | }
240 |
241 | /**
242 | * @brief Checks if a directory in the OMEMO directory structure exists
243 | * and returns the path.
244 | *
245 | * @param local_user The local OMEMO user
246 | * @param user The user whose directory to find or `NULL` if the directory
247 | * of the local user should be found
248 | * @param file The file's name to find or `NULL` to only look for
249 | * the directory
250 | * @param buffer The allocated buffer to write the path to
251 | * @param buf_len The lenght of the buffer in bytes
252 | *
253 | * @retval 0 The directory was found and correctly written to buffer
254 | * @retval 1 The directory was not found but the correct path was
255 | * written to the buffer
256 | * @retval -1 An error occurred and errno is set to indicate the
257 | * cause
258 | */
259 |
260 | static int omemo_get_dir(const signal_protocol_address *local_user,
261 | const signal_protocol_address *user,
262 | const char *file, char *buffer, size_t buf_len)
263 | {
264 | if (!local_user || !buffer || !buf_len) {
265 | errno = EINVAL;
266 | return -1;
267 | }
268 |
269 | size_t bytes_written;
270 | struct stat st = {0};
271 |
272 | bytes_written = snprintf(buffer, buf_len, "%s/%s/%s/%d",
273 | getenv("HOME"), OMEMO_WORKING_DIR,
274 | local_user->name, local_user->device_id);
275 |
276 | if (!user || !strncmp(local_user->name, user->name,
277 | local_user->name_len)) {
278 | /* local user and searched user are the same */
279 | if (file) {
280 | bytes_written += snprintf(buffer + bytes_written,
281 | buf_len - bytes_written,
282 | "/%s", file);
283 | }
284 | } else {
285 | bytes_written += (!strcmp(file, dev_file_name)
286 | || !user->device_id)
287 | ? snprintf(buffer+bytes_written, buf_len-bytes_written,
288 | "/%s/%s", "contacts", user->name)
289 | : snprintf(buffer+bytes_written, buf_len-bytes_written,
290 | "/%s/%s/%d", "contacts", user->name,
291 | user->device_id)
292 | ;
293 |
294 | if (file) {
295 | bytes_written += snprintf(buffer + bytes_written,
296 | buf_len - bytes_written,
297 | "/%s", file);
298 | }
299 | }
300 |
301 | if (bytes_written >= buf_len) {
302 | errno = ENOBUFS;
303 | return -1;
304 | }
305 | if (stat(buffer, &st) < 0) {
306 | return 1;
307 | }
308 |
309 | return 0;
310 | }
311 |
312 | /**
313 | * @brief Deletes a node in file tree walk
314 | *
315 | * @retval 0 The node was removed successfully
316 | * @retval -1 An error occurred while removing the node
317 | */
318 | int omemo_delete_cb(const char *fpath, const struct stat *sb, int typeflag,
319 | struct FTW *ftwbuf)
320 | {
321 | if (remove(fpath))
322 | return -1;
323 | else
324 | return 0;
325 | }
326 |
327 | /**
328 | * @brief Deletes a directory recursively
329 | *
330 | * @param path The path to delete
331 | *
332 | * @retval 0 The directory was removed successfully
333 | * @retval -1 An error occurred while removing the directory
334 | */
335 | int omemo_rm_dir(const char *path)
336 | {
337 | return nftw(path, omemo_delete_cb, 64, FTW_DEPTH | FTW_PHYS);
338 | }
339 |
340 |
341 | int omemo_store_device_list(const signal_protocol_address *user,
342 | struct device_list *list)
343 | {
344 | char path[PATH_MAX];
345 | FILE *devices;
346 | int retval;
347 | size_t devices_written = 0;
348 | struct device_list *cur;
349 |
350 | retval = omemo_get_dir(user, &(list->device->address), dev_file_name,
351 | path, sizeof(path));
352 |
353 | puts(path);
354 | if (retval < 0) {
355 | return -1;
356 | }
357 |
358 | if (retval) {
359 | if (omemo_mk_dir(path) != 0) {
360 | return -1;
361 | }
362 | }
363 |
364 | devices = fopen(path, "w+");
365 | if (!devices) {
366 | return -1;
367 | }
368 |
369 | if (fputs("A:", devices) == EOF) {
370 | return -1;
371 | }
372 |
373 | for (cur = list; cur != NULL; cur = cur->next) {
374 | if (cur->device->status == ACTIVE) {
375 | if (devices_written) {
376 | fputs(",", devices);
377 | }
378 | fprintf(devices, "%d", cur->device->address.device_id);
379 | devices_written++;
380 | }
381 | }
382 |
383 | if (fputs("\nI:", devices) == EOF) {
384 | return -1;
385 | }
386 |
387 | devices_written = 0;
388 | for (cur = list; cur != NULL; cur = cur->next) {
389 | if (cur->device->status == INACTIVE) {
390 | if (devices_written) {
391 | fputs(",", devices);
392 | }
393 | fprintf(devices, "%d", cur->device->address.device_id);
394 | devices_written++;
395 | }
396 | }
397 |
398 | fclose(devices);
399 |
400 | return 0;
401 |
402 | }
403 |
404 | int omemo_is_local_user_existent(const signal_protocol_address *address)
405 | {
406 | int retval;
407 | char buffer[PATH_MAX];
408 |
409 | if (!address) {
410 | errno = EINVAL;
411 | return -1;
412 | }
413 |
414 | retval = omemo_get_dir(address, NULL, NULL, buffer, sizeof(buffer));
415 | if (retval < 0) {
416 | return -1;
417 | }
418 |
419 | return !retval;
420 | }
421 |
422 |
423 | int omemo_load_session(signal_buffer **record, signal_buffer **user_record,
424 | const signal_protocol_address *address, void *user_data)
425 | {
426 | if (!address || !user_data) {
427 | return SG_ERR_INVAL;
428 | }
429 |
430 | int retval;
431 | char path_buf[PATH_MAX];
432 | FILE *se_file = NULL;
433 | uint8_t *se_data;
434 | struct stat st = {0};
435 | struct omemo_context *context = user_data;
436 |
437 |
438 | retval = omemo_get_dir(&context->own_address, address, se_file_name, path_buf,
439 | sizeof(path_buf));
440 |
441 | if (retval <= 0) {
442 | return (retval == 0) ? retval : SG_ERR_UNKNOWN;
443 | }
444 |
445 | se_file = fopen(path_buf, "r");
446 | if (!se_file) {
447 | return SG_ERR_UNKNOWN;
448 | }
449 |
450 | /* Determine size of file */
451 | if (fstat(fileno(se_file), &st)) {
452 | fclose(se_file);
453 | return SG_ERR_UNKNOWN;
454 | }
455 |
456 | se_data = malloc(st.st_size);
457 | if (!se_data) {
458 | fclose(se_file);
459 | return SG_ERR_NOMEM;
460 | }
461 |
462 | if (fread(se_data, 1, st.st_size, se_file) != st.st_size) {
463 | goto err_cleanup;
464 | }
465 |
466 | *record = signal_buffer_create(se_data, st.st_size);
467 | if (!(*record)) {
468 | goto err_cleanup;
469 | }
470 |
471 | fclose(se_file);
472 | free(se_data);
473 | return 1;
474 |
475 | err_cleanup:
476 | fclose(se_file);
477 | free(se_data);
478 | return SG_ERR_UNKNOWN;
479 | }
480 |
481 |
482 | int omemo_get_sub_device_sessions(signal_int_list **sessions, const char *name,
483 | size_t name_len, void *user_data)
484 | {
485 | char buffer[PATH_MAX];
486 | DIR *dir;
487 | int size, retval;
488 | struct dirent *folder;
489 | struct signal_protocol_address user_addr = {
490 | .name = name,
491 | .name_len = name_len,
492 | .device_id = 0
493 | };
494 | struct omemo_context *context = user_data;
495 |
496 | size = 0;
497 |
498 | if (!name || !user_data)
499 | return SG_ERR_INVAL;
500 |
501 | memset(buffer, 0x0, sizeof(buffer));
502 |
503 | retval = omemo_get_dir(&context->own_address, &user_addr, NULL, buffer, sizeof(buffer));
504 | if (retval < 0) {
505 | return SG_ERR_UNKNOWN;
506 | } else if (!retval) {
507 | return 0;
508 | }
509 |
510 | dir = opendir(buffer);
511 | if (!dir)
512 | return SG_ERR_UNKNOWN;
513 |
514 | *sessions = signal_int_list_alloc();
515 | if (!*sessions)
516 | return SG_ERR_UNKNOWN;
517 |
518 | while ((folder = readdir(dir))) {
519 | int device_id = atoi(folder->d_name);
520 | signal_int_list_push_back(*sessions, device_id);
521 | ++size;
522 | }
523 |
524 | closedir(dir);
525 | return size;
526 | }
527 |
528 | int omemo_store_session(const signal_protocol_address *address, uint8_t *record,
529 | size_t record_len, uint8_t *user_record, size_t user_record_len,
530 | void *user_data)
531 | {
532 | char buffer[PATH_MAX];
533 | FILE *se_file;
534 | int retval;
535 | size_t bytes_written;
536 | struct omemo_context *context = user_data;
537 |
538 | if (!address || !record || !user_data)
539 | return SG_ERR_INVAL;
540 |
541 | memset(buffer, 0x0, sizeof(buffer));
542 |
543 | retval = omemo_get_dir(&context->own_address, address, se_file_name, buffer,
544 | sizeof(buffer));
545 | if (retval < 0)
546 | return SG_ERR_UNKNOWN;
547 |
548 | se_file = fopen(buffer, "w");
549 | if (!se_file)
550 | return SG_ERR_UNKNOWN;
551 |
552 | bytes_written = fwrite(record, 1, record_len, se_file);
553 | if (bytes_written < record_len) {
554 | fclose(se_file);
555 | return SG_ERR_UNKNOWN;
556 | }
557 |
558 | fclose(se_file);
559 | return 0;
560 | }
561 |
562 | int omemo_contains_session(const signal_protocol_address *address,
563 | void *user_data)
564 | {
565 | char buffer[PATH_MAX];
566 | int retval;
567 | struct omemo_context *context = user_data;
568 |
569 | if (!address || !user_data)
570 | return SG_ERR_INVAL;
571 |
572 | retval = omemo_get_dir(&context->own_address, address, NULL, buffer, sizeof(buffer));
573 |
574 | return (retval == 0) ? 1 : 0;
575 | }
576 |
577 | int omemo_delete_session(const signal_protocol_address *address,
578 | void *user_data)
579 | {
580 | char buffer[PATH_MAX];
581 | int retval;
582 | struct omemo_context *context = user_data;
583 |
584 | if (!address || !user_data)
585 | return SG_ERR_INVAL;
586 |
587 | memset(buffer, 0x0, sizeof(buffer));
588 |
589 | retval = omemo_get_dir(&context->own_address, address, NULL, buffer, sizeof(buffer));
590 | if (retval < 0)
591 | return SG_ERR_UNKNOWN;
592 | else if (retval)
593 | return 0;
594 |
595 | return omemo_rm_dir(buffer);
596 | }
597 |
598 | int omemo_delete_all_sessions(const char *name, size_t name_len,
599 | void *user_data)
600 | {
601 | char buffer[PATH_MAX];
602 | DIR *dir;
603 | int retval, se_deleted;
604 | struct dirent *folder;
605 | signal_protocol_address user_addr = {
606 | .name = name,
607 | .name_len = name_len,
608 | .device_id = 0
609 | };
610 | struct omemo_context *context = user_data;
611 |
612 | se_deleted = 0;
613 |
614 | if (!name || !user_data) {
615 | return SG_ERR_INVAL;
616 | }
617 |
618 | memset(buffer, 0x0, sizeof(buffer));
619 |
620 | retval = omemo_get_dir(&context->own_address, &user_addr, NULL, buffer, sizeof(buffer));
621 | if (retval < 0)
622 | return SG_ERR_UNKNOWN;
623 | else if (retval)
624 | return 0;
625 |
626 | dir = opendir(buffer);
627 | if (!dir)
628 | return SG_ERR_UNKNOWN;
629 |
630 | while ((folder = readdir(dir)))
631 | ++se_deleted;
632 |
633 | retval = omemo_rm_dir(buffer);
634 | if (retval < 0)
635 | return retval;
636 | else
637 | return se_deleted;
638 | }
639 |
640 | void omemo_session_store_destroy(void *user_data)
641 | { }
642 |
643 | int omemo_load_pre_key(signal_buffer **record, uint32_t pre_key_id,
644 | void *user_data)
645 | {
646 | char path_buf[PATH_MAX];
647 | FILE *pk_file;
648 | int retval;
649 | struct stat st;
650 | uint8_t *buffer;
651 | struct omemo_context *context = user_data;
652 |
653 | if (!user_data) {
654 | return SG_ERR_INVAL;
655 | }
656 |
657 | memset(path_buf, 0x0, sizeof(path_buf));
658 | memset(&st, 0x0, sizeof(struct stat));
659 |
660 | retval = omemo_get_pre_key_store(&context->own_address, path_buf, sizeof(path_buf));
661 | if (retval)
662 | return SG_ERR_INVALID_KEY_ID;
663 |
664 | pk_file = fopen(path_buf, "r");
665 | if (!pk_file)
666 | return SG_ERR_INVALID_KEY_ID;
667 |
668 | /* Determine file size */
669 | if (fstat(fileno(pk_file), &st) < 0) {
670 | fclose(pk_file);
671 | return SG_ERR_INVALID_KEY_ID;
672 | }
673 |
674 | buffer = malloc(st.st_size);
675 | if (!buffer) {
676 | fclose(pk_file);
677 | return SG_ERR_INVALID_KEY_ID;
678 | }
679 |
680 | if (fread(buffer, 1, st.st_size, pk_file) < st.st_size)
681 | goto err_cleanup;
682 |
683 | *record = signal_buffer_create(buffer, st.st_size);
684 | if (!(*record))
685 | goto err_cleanup;
686 |
687 | fclose(pk_file);
688 | free(buffer);
689 | return SG_SUCCESS;
690 |
691 | err_cleanup:
692 |
693 | fclose(pk_file);
694 | free(buffer);
695 | return SG_ERR_INVALID_KEY_ID;
696 | }
697 |
698 | int omemo_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len,
699 | void *user_data)
700 | {
701 | char buffer[PATH_MAX];
702 | FILE *pk_file;
703 | int retval;
704 | size_t bytes_free, bytes_written;
705 | struct omemo_context *context = user_data;
706 |
707 | if (!record || !user_data)
708 | return SG_ERR_INVAL;
709 |
710 | memset(buffer, 0x0, sizeof(buffer));
711 |
712 | retval = omemo_get_pre_key_store(&context->own_address, buffer, sizeof(buffer));
713 | if (retval < 0)
714 | return SG_ERR_UNKNOWN;
715 | else if (retval) {
716 | retval = omemo_mk_dir(buffer);
717 | if (retval < 0)
718 | return SG_ERR_UNKNOWN;
719 | }
720 |
721 | bytes_free = sizeof(buffer) - strlen(buffer);
722 | if (snprintf(buffer, bytes_free, "%d", pre_key_id) >= bytes_free)
723 | return SG_ERR_UNKNOWN;
724 |
725 | pk_file = fopen(buffer, "w");
726 | if (!pk_file)
727 | return SG_ERR_UNKNOWN;
728 |
729 | bytes_written = fwrite(record, 1, record_len, pk_file);
730 | if (bytes_written < record_len) {
731 | fclose(pk_file);
732 | return SG_ERR_UNKNOWN;
733 | }
734 |
735 | fclose(pk_file);
736 | return 0;
737 | }
738 |
739 | int omemo_contains_pre_key(uint32_t pre_key_id, void *user_data)
740 | {
741 | char buffer[PATH_MAX];
742 | int retval;
743 | size_t bytes_free;
744 | struct stat st = {0};
745 | struct omemo_context *context = user_data;
746 |
747 | if (!user_data) {
748 | return SG_ERR_INVAL;
749 | }
750 |
751 | memset(buffer, 0x0, sizeof(buffer));
752 |
753 | retval = omemo_get_pre_key_store(&context->own_address, buffer, sizeof(buffer));
754 | if (retval < 0)
755 | return SG_ERR_UNKNOWN;
756 | else if (retval)
757 | return 0;
758 |
759 | bytes_free = sizeof(buffer) - strlen(buffer);
760 | if (snprintf(buffer, bytes_free, "%d", pre_key_id) >= bytes_free)
761 | return SG_ERR_UNKNOWN;
762 |
763 | return (stat(buffer, &st) < 0) ? 0 : 1;
764 | }
765 |
766 | int omemo_remove_pre_key(uint32_t pre_key_id, void *user_data)
767 | {
768 | char buffer[PATH_MAX];
769 | int retval;
770 | size_t bytes_free;
771 | struct stat st = {0};
772 | struct omemo_context *context = user_data;
773 |
774 | if (!user_data) {
775 | return SG_ERR_INVAL;
776 | }
777 |
778 | memset(buffer, 0x0, sizeof(buffer));
779 |
780 | retval = omemo_get_pre_key_store(&context->own_address, buffer, sizeof(buffer));
781 | if (retval)
782 | return SG_ERR_UNKNOWN;
783 |
784 | bytes_free = sizeof(buffer) - strlen(buffer);
785 | if (snprintf(buffer, bytes_free, "%d", pre_key_id) >= bytes_free)
786 | return SG_ERR_UNKNOWN;
787 |
788 | if (stat(buffer, &st) < 0)
789 | return SG_ERR_UNKNOWN;
790 |
791 | retval = remove(buffer);
792 | if (retval < 0)
793 | return SG_ERR_UNKNOWN;
794 |
795 | return 0;
796 | }
797 |
798 | void omemo_pre_key_store_destroy(void *user_data)
799 | { }
800 |
801 |
802 | int omemo_load_signed_pre_key(signal_buffer **record,
803 | uint32_t signed_pre_key_id, void *user_data)
804 | {
805 | char path_buf[PATH_MAX];
806 | FILE *pk_file;
807 | int retval;
808 | struct stat st;
809 | uint8_t *buffer;
810 | struct omemo_context *context = user_data;
811 |
812 | if (!user_data) {
813 | return SG_ERR_INVAL;
814 | }
815 |
816 | memset(path_buf, 0x0, sizeof(path_buf));
817 | memset(&st, 0x0, sizeof(struct stat));
818 |
819 | retval = omemo_get_signed_pre_key_store(&context->own_address, path_buf,
820 | sizeof(path_buf));
821 | if (retval)
822 | return SG_ERR_INVALID_KEY_ID;
823 |
824 | pk_file = fopen(path_buf, "r");
825 | if (!pk_file)
826 | return SG_ERR_INVALID_KEY_ID;
827 |
828 | /* Determine file size */
829 | if (fstat(fileno(pk_file), &st) < 0) {
830 | fclose(pk_file);
831 | return SG_ERR_INVALID_KEY_ID;
832 | }
833 |
834 | buffer = malloc(st.st_size);
835 | if (!buffer) {
836 | fclose(pk_file);
837 | return SG_ERR_INVALID_KEY_ID;
838 | }
839 |
840 | if (fread(buffer, 1, st.st_size, pk_file) < st.st_size)
841 | goto err_cleanup;
842 |
843 | *record = signal_buffer_create(buffer, st.st_size);
844 | if (!(*record))
845 | goto err_cleanup;
846 |
847 | fclose(pk_file);
848 | free(buffer);
849 | return SG_SUCCESS;
850 |
851 | err_cleanup:
852 |
853 | fclose(pk_file);
854 | free(buffer);
855 | return SG_ERR_INVALID_KEY_ID;
856 | }
857 |
858 | int omemo_store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record,
859 | size_t record_len, void *user_data)
860 | {
861 | char buffer[PATH_MAX];
862 | FILE *pk_file;
863 | int retval;
864 | size_t bytes_free, bytes_written;
865 | struct omemo_context *context = user_data;
866 |
867 | if (!record || !user_data)
868 | return SG_ERR_INVAL;
869 |
870 | memset(buffer, 0x0, sizeof(buffer));
871 |
872 | retval = omemo_get_signed_pre_key_store(&context->own_address, buffer, sizeof(buffer));
873 | if (retval < 0)
874 | return SG_ERR_UNKNOWN;
875 | else if (retval) {
876 | retval = omemo_mk_dir(buffer);
877 | if (retval < 0)
878 | return SG_ERR_UNKNOWN;
879 | }
880 |
881 | bytes_free = sizeof(buffer) - strlen(buffer);
882 | if (snprintf(buffer, bytes_free, "%d", signed_pre_key_id)
883 | >= bytes_free)
884 | return SG_ERR_UNKNOWN;
885 |
886 | pk_file = fopen(buffer, "w");
887 | if (!pk_file)
888 | return SG_ERR_UNKNOWN;
889 |
890 | bytes_written = fwrite(record, 1, record_len, pk_file);
891 | if (bytes_written < record_len) {
892 | fclose(pk_file);
893 | return SG_ERR_UNKNOWN;
894 | }
895 |
896 | fclose(pk_file);
897 | return 0;
898 | }
899 |
900 | int omemo_contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
901 | {
902 | char buffer[PATH_MAX];
903 | int retval;
904 | size_t bytes_free;
905 | struct stat st = {0};
906 | struct omemo_context *context = user_data;
907 |
908 | if (!user_data) {
909 | return SG_ERR_INVAL;
910 | }
911 |
912 | memset(buffer, 0x0, sizeof(buffer));
913 |
914 | retval = omemo_get_signed_pre_key_store(&context->own_address, buffer, sizeof(buffer));
915 | if (retval < 0)
916 | return SG_ERR_UNKNOWN;
917 | else if (retval)
918 | return 0;
919 |
920 | bytes_free = sizeof(buffer) - strlen(buffer);
921 | if (snprintf(buffer, bytes_free, "%d", signed_pre_key_id)
922 | >= bytes_free)
923 | return SG_ERR_UNKNOWN;
924 |
925 | return (stat(buffer, &st) < 0) ? 0 : 1;
926 | }
927 |
928 | int omemo_remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
929 | {
930 | char buffer[PATH_MAX];
931 | int retval;
932 | size_t bytes_free;
933 | struct stat st = {0};
934 | struct omemo_context *context = user_data;
935 |
936 | if (!user_data) {
937 | return SG_ERR_INVAL;
938 | }
939 |
940 | memset(buffer, 0x0, sizeof(buffer));
941 |
942 | retval = omemo_get_signed_pre_key_store(&context->own_address, buffer, sizeof(buffer));
943 | if (retval)
944 | return SG_ERR_UNKNOWN;
945 |
946 | bytes_free = sizeof(buffer) - strlen(buffer);
947 | if (snprintf(buffer, bytes_free, "%d", signed_pre_key_id)
948 | >= bytes_free)
949 | return SG_ERR_UNKNOWN;
950 |
951 | if (stat(buffer, &st) < 0)
952 | return SG_ERR_UNKNOWN;
953 |
954 | retval = remove(buffer);
955 | if (retval < 0)
956 | return SG_ERR_UNKNOWN;
957 |
958 | return 0;
959 | }
960 |
961 | void omemo_signed_pre_key_store_destroy(void *user_data)
962 | { }
963 |
964 |
965 | int omemo_get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data,
966 | void *user_data)
967 | {
968 | char buffer[PATH_MAX];
969 | FILE *priv_key_file;
970 | FILE *pub_key_file;
971 | int retval;
972 | size_t path_len;
973 | size_t bytes_free;
974 | uint8_t *priv_key_buffer;
975 | uint8_t *pub_key_buffer;
976 | struct stat st = {0};
977 | struct omemo_context *context = user_data;
978 |
979 | if (!user_data) {
980 | return SG_ERR_INVAL;
981 | }
982 |
983 | memset(buffer, 0x0, sizeof(buffer));
984 |
985 | retval = omemo_get_identity_key_store(&context->own_address, buffer, sizeof(buffer));
986 | if (retval)
987 | return SG_ERR_UNKNOWN;
988 |
989 | path_len = strlen(buffer);
990 | bytes_free = sizeof(buffer) - path_len;
991 |
992 | /* Load private key */
993 | if (snprintf(buffer + path_len, bytes_free, "%s", id_priv_key_file_name)
994 | >= bytes_free) {
995 | return SG_ERR_UNKNOWN;
996 | }
997 |
998 | priv_key_file = fopen(buffer, "r");
999 | if (!priv_key_file) {
1000 | return SG_ERR_INVALID_KEY_ID;
1001 | }
1002 |
1003 | if (fstat(fileno(priv_key_file), &st) < 0) {
1004 | fclose(priv_key_file);
1005 | return SG_ERR_INVALID_KEY_ID;
1006 | }
1007 |
1008 | priv_key_buffer = malloc(st.st_size);
1009 | if (!priv_key_buffer) {
1010 | fclose(priv_key_file);
1011 | return SG_ERR_UNKNOWN;
1012 | }
1013 |
1014 | if (fread(priv_key_buffer, 1, st.st_size, priv_key_file) < st.st_size) {
1015 | fclose(priv_key_file);
1016 | free(priv_key_buffer);
1017 | return SG_ERR_UNKNOWN;
1018 | }
1019 |
1020 | *private_data = signal_buffer_create(priv_key_buffer, st.st_size);
1021 | if (!(*private_data)) {
1022 | fclose(priv_key_file);
1023 | free(priv_key_buffer);
1024 | return SG_ERR_UNKNOWN;
1025 | }
1026 |
1027 | fclose(priv_key_file);
1028 |
1029 | /* Load public key */
1030 | if (snprintf(buffer + path_len, bytes_free, "%s", id_pub_key_file_name)
1031 | >= bytes_free) {
1032 | return SG_ERR_UNKNOWN;
1033 | }
1034 |
1035 | pub_key_file = fopen(buffer, "r");
1036 | if (!pub_key_file) {
1037 | return SG_ERR_INVALID_KEY_ID;
1038 | }
1039 |
1040 | if (fstat(fileno(pub_key_file), &st) < 0) {
1041 | fclose(pub_key_file);
1042 | return SG_ERR_INVALID_KEY_ID;
1043 | }
1044 |
1045 | pub_key_buffer = malloc(st.st_size);
1046 | if (!pub_key_buffer) {
1047 | fclose(pub_key_file);
1048 | return SG_ERR_UNKNOWN;
1049 | }
1050 |
1051 | if (fread(pub_key_buffer, 1, st.st_size, pub_key_file) < st.st_size) {
1052 | fclose(pub_key_file);
1053 | free(pub_key_buffer);
1054 | return SG_ERR_UNKNOWN;
1055 | }
1056 |
1057 | *public_data = signal_buffer_create(pub_key_buffer, st.st_size);
1058 | if (!(*public_data)) {
1059 | fclose(pub_key_file);
1060 | free(pub_key_buffer);
1061 | return SG_ERR_UNKNOWN;
1062 | }
1063 |
1064 | fclose(pub_key_file);
1065 |
1066 | return SG_SUCCESS;
1067 | }
1068 |
1069 | int omemo_get_local_registration_id(void *user_data, uint32_t *registration_id)
1070 | {
1071 | if (!registration_id) {
1072 | return SG_ERR_INVAL;
1073 | }
1074 |
1075 | /* default value for OMEMO, only important for signal */
1076 | *registration_id = 0;
1077 |
1078 | return SG_SUCCESS;
1079 | }
1080 |
1081 | int omemo_save_identity(const signal_protocol_address *address, uint8_t *key_data,
1082 | size_t key_len, void *user_data)
1083 | {
1084 | /* Not needed as OMEMO handles device management */
1085 |
1086 | return SG_SUCCESS;
1087 | }
1088 |
1089 | int omemo_is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data,
1090 | size_t key_len, void *user_data)
1091 | {
1092 | /* Trust management is done by OMEMO */
1093 | return 1;
1094 | }
1095 |
1096 | void omemo_identity_key_store_destroy(void *user_data)
1097 | {
1098 | }
1099 |
--------------------------------------------------------------------------------
/store/omemo_store.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_STORE_H
2 | #define PROF_OMEMO_STORE_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | struct omemo_store_context {
11 | signal_protocol_session_store session_store;
12 | signal_protocol_pre_key_store pre_key_store;
13 | signal_protocol_signed_pre_key_store signed_pre_key_store;
14 | signal_protocol_identity_key_store identity_key_store;
15 | };
16 |
17 | struct omemo_context;
18 | struct device_list;
19 |
20 | struct omemo_store_context *omemo_store_context_create(struct omemo_context *context);
21 |
22 | /**
23 | * @brief Stores a device list persistently
24 | *
25 | * @param user Local user
26 | * @param list Device list to store
27 | *
28 | * @retval 0 The device list was stored successfully
29 | * @retval -1 An error occurred and `errno` was set to indicate the cause
30 | */
31 | int omemo_store_device_list(const signal_protocol_address *user,
32 | struct device_list *list);
33 |
34 | int omemo_is_local_user_existent(const signal_protocol_address *address);
35 |
36 | /* From Libsignal for signal_protocol_session_store */
37 | int omemo_load_session(signal_buffer **record, signal_buffer **user_record,
38 | const signal_protocol_address *address, void *user_data);
39 |
40 | int omemo_get_sub_device_sessions(signal_int_list **sessions,
41 | const char *name, size_t name_len,
42 | void *user_data);
43 |
44 | int omemo_store_session(const signal_protocol_address *address, uint8_t *record,
45 | size_t record_len, uint8_t *user_record, size_t user_record_len,
46 | void *user_data);
47 |
48 | int omemo_contains_session(const signal_protocol_address *address,
49 | void *user_data);
50 |
51 | int omemo_delete_session(const signal_protocol_address *address,
52 | void *user_data);
53 |
54 | int omemo_delete_all_sessions(const char *name, size_t name_len,
55 | void *user_data);
56 |
57 | void omemo_session_store_destroy(void *user_data);
58 |
59 |
60 | /* From Libsignal for signal_protocol_pre_key_store */
61 | int omemo_load_pre_key(signal_buffer **record, uint32_t pre_key_id,
62 | void *user_data);
63 |
64 | int omemo_store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len,
65 | void *user_data);
66 |
67 | int omemo_contains_pre_key(uint32_t pre_key_id, void *user_data);
68 |
69 | int omemo_remove_pre_key(uint32_t pre_key_id, void *user_data);
70 |
71 | void omemo_pre_key_store_destroy(void *user_data);
72 |
73 |
74 | /* From Libsignal for signal_protocol_signed_pre_key_store */
75 | int omemo_load_signed_pre_key(signal_buffer **record, uint32_t pre_key_id,
76 | void *user_data);
77 |
78 | int omemo_store_signed_pre_key(uint32_t pre_key_id, uint8_t *record,
79 | size_t record_len, void *user_data);
80 |
81 | int omemo_contains_signed_pre_key(uint32_t pre_key_id, void *user_data);
82 |
83 | int omemo_remove_signed_pre_key(uint32_t pre_key_id, void *user_data);
84 |
85 | void omemo_signed_pre_key_store_destroy(void *user_data);
86 |
87 |
88 | /* From Libsignal for signal_protocol_idendity_key_store */
89 | int omemo_get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data,
90 | void *user_data);
91 |
92 | int omemo_get_local_registration_id(void *user_data, uint32_t *registration_id);
93 |
94 | int omemo_save_identity(const signal_protocol_address *address, uint8_t *key_data,
95 | size_t key_len, void *user_data);
96 |
97 | int omemo_is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data,
98 | size_t key_len, void *user_data);
99 |
100 | void omemo_identity_key_store_destroy(void *user_data);
101 |
102 | #ifdef __cplusplus
103 | }
104 | #endif
105 |
106 | #endif /* PROF_OMEMO_STORE_H */
107 |
--------------------------------------------------------------------------------
/store/targets.cmake:
--------------------------------------------------------------------------------
1 | set (
2 | STORE_SRC
3 | store/omemo_file_store.c
4 | store/omemo_store.h
5 | )
6 |
--------------------------------------------------------------------------------
/structs/device_list.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "device_list.h"
7 | #include "omemo_device.h"
8 |
9 |
10 |
11 | int omemo_device_list_serialize_xml(xmlNodePtr *root, struct device_list **list)
12 | {
13 | struct device_list *cur;
14 | xmlNodePtr list_root = NULL;
15 | xmlNsPtr ns_list = NULL;
16 |
17 |
18 | list_root = xmlNewNode(ns_list, BAD_CAST "list");
19 | ns_list = xmlNewNs(list_root, BAD_CAST OMEMO_DEVICE_LIST_XML_NS, NULL);
20 | if (!ns_list) {
21 | errno = EINVAL;
22 | return -1;
23 | }
24 |
25 | xmlAddChild(*root, list_root);
26 |
27 | for (cur = *list; cur != NULL; cur = cur->next) {
28 | xmlNodePtr device = xmlNewChild(list_root, NULL, BAD_CAST "device", NULL);
29 | char id_str[32];
30 | snprintf(id_str, sizeof(id_str), "%d", cur->device->address.device_id);
31 | xmlSetProp(device, BAD_CAST "id", BAD_CAST id_str);
32 | }
33 |
34 | return 0;
35 | }
36 |
37 | int omemo_device_list_deserialize_xml(struct device_list **head, xmlNodePtr node,
38 | const char *jid)
39 | {
40 | return 0;
41 | }
42 |
43 |
44 | int omemo_device_list_add(struct device_list **head,
45 | struct omemo_device *device)
46 | {
47 | struct device_list *new_device;
48 |
49 | if (!head || !device) {
50 | errno = EINVAL;
51 | return -1;
52 | }
53 |
54 | /* Check if new device is already in list */
55 | if (omemo_device_list_contains(head, device)) {
56 | return 0;
57 | }
58 |
59 |
60 | new_device = malloc(sizeof(struct device_list));
61 |
62 | if (!new_device) {
63 | errno = ENOMEM;
64 | return -1;
65 | }
66 |
67 | new_device->next = *head;
68 | new_device->device = device;
69 |
70 | *head = new_device;
71 |
72 | return 1;
73 | }
74 |
75 | int omemo_device_list_add_inplace(struct device_list **head,
76 | struct device_list *pos,
77 | struct omemo_device *device)
78 | {
79 | struct device_list *new_device, *cur;
80 |
81 | if (!head || !pos || !device) {
82 | errno = EINVAL;
83 | return -1;
84 | }
85 |
86 | if (!omemo_device_list_contains(head, pos->device)) {
87 | errno = EINVAL;
88 | return -1;
89 | }
90 |
91 | if (omemo_device_list_contains(head, device)) {
92 | return 0;
93 | }
94 |
95 |
96 | new_device = malloc(sizeof(struct device_list));
97 |
98 | if (!new_device) {
99 | errno = ENOMEM;
100 | return -1;
101 | }
102 |
103 | new_device->next = pos;
104 | new_device->device = device;
105 |
106 | if (*head == pos) {
107 | *head = new_device;
108 | return 1;
109 | }
110 |
111 | for (cur = *head; cur->next != pos; cur = cur->next) { }
112 |
113 | cur->next = new_device;
114 |
115 | return 1;
116 | }
117 |
118 |
119 | int omemo_device_list_contains(struct device_list **head,
120 | struct omemo_device *device)
121 | {
122 | struct device_list *cur;
123 |
124 | if (!head || !device) {
125 | errno = EINVAL;
126 | return -1;
127 | }
128 |
129 | for (cur = *head; cur != NULL; cur = cur->next) {
130 | if (cur->device == device) {
131 | return 1;
132 | }
133 | }
134 |
135 | return 0;
136 | }
137 |
138 |
139 | int omemo_device_list_contains_id(struct device_list **head,
140 | int32_t id)
141 | {
142 | struct device_list *cur;
143 |
144 | if (!head || !id) {
145 | errno = EINVAL;
146 | return -1;
147 | }
148 |
149 | for (cur = *head; cur != NULL; cur = cur->next) {
150 | if (cur->device->address.device_id == id) {
151 | return 1;
152 | }
153 | }
154 |
155 | return 0;
156 | }
157 |
158 |
159 | int omemo_device_list_remove(struct device_list **head,
160 | struct omemo_device *device)
161 | {
162 | struct device_list *cur;
163 |
164 | if (!head || !device) {
165 | errno = EINVAL;
166 | return -1;
167 | }
168 |
169 | if ((*head)->device == device) {
170 | struct device_list *next = (*head)->next;
171 | free(*head);
172 | *head = next;
173 | return 0;
174 | }
175 |
176 | for (cur = *head; cur->next != NULL; cur = cur->next) {
177 | if (cur->next->device == device) {
178 | struct device_list *tmp = cur->next;
179 | cur->next = cur->next->next;
180 | free(tmp);
181 | return 0;
182 | }
183 | }
184 |
185 | errno = EINVAL;
186 |
187 | return -1;
188 | }
189 |
190 |
191 | uint32_t omemo_device_list_size(struct device_list **head)
192 | {
193 | if (!head) {
194 | return 0;
195 | }
196 |
197 | uint32_t size = 0;
198 | struct device_list *cur;
199 |
200 | for (cur = *head; cur != NULL; ++size, cur = cur->next)
201 | { }
202 |
203 | return size;
204 | }
205 |
206 | int omemo_device_list_free_device(struct device_list **head,
207 | struct omemo_device *device)
208 | {
209 | int retval;
210 | retval = omemo_device_list_remove(head, device);
211 |
212 | if (retval < 0) {
213 | errno = EINVAL;
214 | return -1;
215 | }
216 |
217 | omemo_device_free(device);
218 |
219 | return 0;
220 | }
221 |
222 | void omemo_device_list_free(struct device_list **head)
223 | {
224 | if (!head || !(*head)) {
225 | return;
226 | }
227 |
228 | while ((*head) != NULL) {
229 | struct device_list *tmp;
230 | omemo_device_free((*head)->device);
231 |
232 | tmp = (*head);
233 | *head = (*head)->next;
234 | free(tmp);
235 | }
236 |
237 | *head = NULL;
238 | }
239 |
--------------------------------------------------------------------------------
/structs/device_list.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_DEVICE_LIST_H
2 | #define PROF_OMEMO_DEVICE_LIST_H
3 |
4 | #include
5 | #include
6 |
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 |
11 | struct omemo_device;
12 |
13 | struct device_list {
14 | struct device_list *next;
15 | struct omemo_device *device;
16 | };
17 |
18 |
19 | /**
20 | * @brief Creates a XML device list
21 | *
22 | * Memory for the buffer has to be freed manually.
23 | *
24 | * @param root XML root node where output should begin
25 | * @param head Device list to serialize
26 | *
27 | * @retval 0 Device list successfully serialized
28 | * @retval -1 An error occurred and `errno` is set to indicate the cause
29 | */
30 | int omemo_device_list_serialize_xml(xmlNodePtr *root, struct device_list **head);
31 |
32 |
33 | /**
34 | * @brief Creates a device list from XML input
35 | *
36 | * @param head Device list to create
37 | * @param node `xmlNodePtr` to start parsing from
38 | * @param jid JID of device owner
39 | *
40 | * @return Number of devices found or `-1` if an error occurred which will
41 | * set `errno` accordingly
42 | */
43 | int omemo_device_list_deserialize_xml(struct device_list **head, xmlNodePtr node,
44 | const char *jid);
45 |
46 |
47 | /**
48 | * @brief Adds an OMEMO device to the begin of the device list.
49 | *
50 | * The list will free the `omeme_device` when it is not needed anymore.
51 | * Avoid freeing it manually or remove it from the list first.
52 | *
53 | * @param head Head of the list, the device shall be added to
54 | * @param device OMEMO device to add to the list
55 | *
56 | * @retval 1 The device was added to the list successfully
57 | * @retval 0 No errors occured but the device or an device with the same ID
58 | * is already in list.
59 | * @retval -1 An error occurred while adding the device to the list. `errno`
60 | * is set to indicated the cause.
61 | */
62 | int omemo_device_list_add(struct device_list **head,
63 | struct omemo_device *device);
64 |
65 | /**
66 | * @brief Adds an OMEMO device at the current position of the device list.
67 | *
68 | * The list will free the `omeme_device` when it is not needed anymore.
69 | * Avoid freeing it manually or remove it from the list first.
70 | *
71 | * @param head Head of the list to insert to
72 | * @param pos Position where the OMEMO device shall be inserted
73 | * @param device OMEMO device to add
74 | *
75 | * @retval 1 The device was added to the list successfully
76 | * @retval 0 No errors occured but the device or an device with the same ID
77 | * is already in list.
78 | * @retval -1 An error occurred while adding the device to the list. `errno`
79 | * is set to indicated the cause.
80 | */
81 | int omemo_device_list_add_inplace(struct device_list **head,
82 | struct device_list *pos,
83 | struct omemo_device *device);
84 |
85 | /**
86 | * @brief Checks if the list contains a device.
87 | *
88 | * @param head Head of the list to look in
89 | * @param device device to look for.
90 | *
91 | * @retval 1 The list contains the device
92 | * @retval 0 The list does not contain the device
93 | * @retval -1 An error occurred and `errno` is set to indicate the cause
94 | */
95 | int omemo_device_list_contains(struct device_list **head,
96 | struct omemo_device *device);
97 |
98 | /**
99 | * @brief Checks if the list contains a device with the ID.
100 | *
101 | * @param head Head of the list to look in
102 | * @param id device ID to look for.
103 | *
104 | * @retval 1 The list contains the device
105 | * @retval 0 The list does not contain the device
106 | * @retval -1 An error occurred and `errno` is set to indicate the cause
107 | */
108 | int omemo_device_list_contains_id(struct device_list **head,
109 | int32_t id);
110 |
111 | /**
112 | * @brief Removes a device from the list.
113 | *
114 | * @param head Head of the list to remove from
115 | * @param device Device to remove
116 | *
117 | * @retval 0 The device was successfully removed from the list
118 | * @retval -1 An error occurred and `errno` is set to indicate
119 | * the cause
120 | */
121 | int omemo_device_list_remove(struct device_list **head,
122 | struct omemo_device *device);
123 |
124 | /**
125 | * @brief Get the number of devices in the list.
126 | *
127 | * @param head Head of the list to count devices in
128 | *
129 | * @return Number of devices in list.
130 | */
131 | uint32_t omemo_device_list_size(struct device_list **head);
132 |
133 |
134 | /**
135 | * @brief Frees a device from the list.
136 | *
137 | * @param head Head of the list to free from
138 | * @param device Device to free
139 | *
140 | * @retval 0 The device was successfully removed from the list and freed
141 | * @retval -1 An error occurred and `errno` is set to indicate the cause
142 | */
143 | int omemo_device_list_free_device(struct device_list **head,
144 | struct omemo_device *device);
145 |
146 | /**
147 | * @brief Frees the device list and all devices it contains.
148 | *
149 | * @param head Head of the list to free
150 | */
151 | void omemo_device_list_free(struct device_list **head);
152 |
153 | #ifdef __cplusplus
154 | }
155 | #endif
156 |
157 | #endif /* PROF_OMEMO_DEVICE_LIST_H */
158 |
--------------------------------------------------------------------------------
/structs/omemo_context.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "omemo_context.h"
7 |
8 | #define UNUSED(x) (void)(x)
9 |
10 | static void omemo_lock(void *user_data)
11 | {
12 | struct omemo_context *context = user_data;
13 |
14 | pthread_mutex_lock(&context->mutex);
15 | }
16 |
17 | static void omemo_unlock(void *user_data)
18 | {
19 | struct omemo_context *context = user_data;
20 |
21 | pthread_mutex_unlock(&context->mutex);
22 | }
23 |
24 | struct omemo_context *omemo_context_create(const signal_protocol_address *address)
25 | {
26 | int retval;
27 | struct omemo_context *context;
28 |
29 | if (!address) {
30 | errno = EINVAL;
31 | return NULL;
32 | }
33 |
34 | context = malloc(sizeof(struct omemo_context));
35 | if (!context) {
36 | errno = ENOMEM;
37 | return NULL;
38 | }
39 |
40 | memcpy(&context->own_address, address, sizeof(signal_protocol_address));
41 | context->store_context = omemo_store_context_create(context);
42 | if (!context->store_context) {
43 | return NULL;
44 | }
45 |
46 | retval = signal_context_create(&context->signal_ctx, context);
47 | if (retval < 0) {
48 | errno = ENOMEM;
49 | return NULL;
50 | }
51 |
52 | retval = signal_context_set_crypto_provider(context->signal_ctx, &omemo_crypto_provider);
53 | if (retval < 0) {
54 | return NULL;
55 | }
56 |
57 | pthread_mutex_init(&context->mutex, NULL);
58 | retval = signal_context_set_locking_functions(context->signal_ctx, omemo_lock, omemo_unlock);
59 | if (retval < 0) {
60 | return NULL;
61 | }
62 |
63 | retval = signal_protocol_store_context_create(&context->signal_store_ctx, context->signal_ctx);
64 | if (retval < 0) {
65 | return NULL;
66 | }
67 |
68 | retval = signal_protocol_store_context_set_session_store(context->signal_store_ctx,
69 | &context->store_context->session_store);
70 | if (retval < 0) {
71 | return NULL;
72 | }
73 |
74 | retval = signal_protocol_store_context_set_pre_key_store(context->signal_store_ctx,
75 | &context->store_context->pre_key_store);
76 | if (retval < 0) {
77 | return NULL;
78 | }
79 |
80 | retval = signal_protocol_store_context_set_signed_pre_key_store(context->signal_store_ctx,
81 | &context->store_context->signed_pre_key_store);
82 | if (retval < 0) {
83 | return NULL;
84 | }
85 |
86 | retval = signal_protocol_store_context_set_identity_key_store(context->signal_store_ctx,
87 | &context->store_context->identity_key_store);
88 | if (retval < 0) {
89 | return NULL;
90 | }
91 |
92 | return context;
93 | }
94 |
95 | void omemo_context_free(struct omemo_context *context)
96 | {
97 | if (!context) {
98 | return;
99 | }
100 |
101 | signal_protocol_store_context_destroy(context->signal_store_ctx);
102 | signal_context_destroy(context->signal_ctx);
103 | pthread_mutex_destroy(&context->mutex);
104 | free(context->store_context);
105 | free(context);
106 | }
107 |
--------------------------------------------------------------------------------
/structs/omemo_context.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_CONTEXT_H
2 | #define PROF_OMEMO_CONTEXT_H
3 |
4 | #include
5 | #include
6 |
7 | #ifdef __cplusplus
8 | extern "C" {
9 | #endif
10 |
11 | struct omemo_context {
12 | signal_protocol_address own_address;
13 | signal_context *signal_ctx;
14 | signal_protocol_store_context *signal_store_ctx;
15 |
16 | struct omemo_store_context *store_context;
17 | struct omemo_bundle *bundle;
18 |
19 | pthread_mutex_t mutex;
20 | };
21 |
22 | struct omemo_context *omemo_context_create(const signal_protocol_address *address);
23 |
24 | void omemo_context_free(struct omemo_context *context);
25 |
26 |
27 |
28 | #ifdef __cplusplus
29 | }
30 | #endif
31 |
32 | #endif /* PROF_OMEMO_CONTEXT_H */
33 |
--------------------------------------------------------------------------------
/structs/omemo_device.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "omemo_device.h"
6 |
7 | struct omemo_device *omemo_device_create(const char *jid, int32_t id)
8 | {
9 | if (!jid) {
10 | errno = EINVAL;
11 | return NULL;
12 | }
13 |
14 | struct omemo_device *new_device = malloc(sizeof(struct omemo_device));
15 |
16 | if (new_device) {
17 | size_t jid_length = strlen(jid);
18 | new_device->address.name = jid;
19 | new_device->address.name_len = jid_length;
20 | new_device->address.device_id = id;
21 |
22 | new_device->trust = UNDECIDED;
23 | new_device->status = ACTIVE;
24 | }
25 |
26 | return new_device;
27 | }
28 |
29 | void omemo_device_free(struct omemo_device *device)
30 | {
31 | free(device);
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/structs/omemo_device.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_DEVICE_H
2 | #define PROF_OMEMO_DEVICE_H
3 |
4 | #include
5 |
6 | #include
7 |
8 | #ifdef __cplusplus
9 | extern "C" {
10 | #endif
11 |
12 | typedef enum _device_trust {
13 | UNDECIDED = 0,
14 | UNTRUSTED,
15 | TRUSTED,
16 | TRUST_COUNT
17 | } device_trust;
18 |
19 | typedef enum _device_status {
20 | INACTIVE = 0,
21 | ACTIVE,
22 | STATUS_COUNT
23 | } device_status;
24 |
25 | struct omemo_device {
26 | signal_protocol_address address;
27 |
28 | device_trust trust;
29 | device_status status;
30 |
31 | /* keys go here */
32 |
33 | };
34 |
35 | /**
36 | * @brief Allocates memory for a new OMEMO device.
37 | *
38 | * @param jid JID the device belongs to.
39 | * @param id ID the device will use.
40 | *
41 | * @return `NULL` if an error occured, a pointer to the created OMEMO device
42 | * otherwise.
43 | */
44 | struct omemo_device *omemo_device_create(const char *jid, int32_t id);
45 |
46 | /**
47 | * @brief Deallocates memory for an OMEMO device.
48 | *
49 | * @param device Pointer to OMEMO device to be deleted.
50 | */
51 | void omemo_device_free(struct omemo_device *device);
52 |
53 | #ifdef __cplusplus
54 | }
55 | #endif
56 |
57 | #endif /* PROF_OMEMO_DEVICE_H */
58 |
--------------------------------------------------------------------------------
/structs/targets.cmake:
--------------------------------------------------------------------------------
1 | set (
2 | STRUCTS_SRC
3 | structs/device_list.h
4 | structs/device_list.c
5 | structs/omemo_context.h
6 | structs/omemo_context.c
7 | structs/omemo_device.h
8 | structs/omemo_device.c
9 | )
10 |
11 |
--------------------------------------------------------------------------------
/test/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/ext/src/gtest/googletest/include/gtest")
2 | include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/ext/src/gtest/googletest/include/gtest/internal")
3 |
4 |
5 | add_executable (test_omemo_crypto test_omemo_crypto.cpp test_main.h)
6 | target_link_libraries (
7 | test_omemo_crypto
8 | profanity-omemo
9 | libgtest
10 | )
11 |
12 | add_executable (test_omemo_device test_omemo_device.cpp test_main.h)
13 | target_link_libraries (
14 | test_omemo_device
15 | profanity-omemo
16 | libgtest
17 | )
18 |
19 | add_executable (test_omemo_device_list test_omemo_device_list.cpp test_main.h)
20 | target_link_libraries (
21 | test_omemo_device_list
22 | profanity-omemo
23 | libgtest
24 | )
25 |
26 | add_executable (test_omemo_pubsub test_omemo_pubsub.cpp test_main.h)
27 | target_link_libraries (
28 | test_omemo_pubsub
29 | profanity-omemo
30 | libgtest
31 | )
32 |
33 | add_executable (test_omemo_store test_omemo_store.cpp test_main.h)
34 | target_link_libraries (
35 | test_omemo_store
36 | profanity-omemo
37 | libgtest
38 | )
39 |
40 | add_test(NAME test_omemo_crypto COMMAND test_omemo_crypto)
41 | add_test(NAME test_omemo_device COMMAND test_omemo_device)
42 | add_test(NAME test_omemo_device_list COMMAND test_omemo_device_list)
43 | add_test(NAME test_omemo_pubsub COMMAND test_omemo_pubsub)
44 | add_test(NAME test_omemo_store COMMAND test_omemo_store)
45 |
--------------------------------------------------------------------------------
/test/test_main.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | int main(int argc, char **argv) {
17 | ::testing::InitGoogleTest(&argc, argv);
18 | return RUN_ALL_TESTS();
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/test/test_omemo_crypto.cpp:
--------------------------------------------------------------------------------
1 | #include "test_main.h"
2 |
3 | TEST(crypto, hmac256)
4 | {
5 | const char *test = "Hello, OMEMO!";
6 | const char *key = "key";
7 |
8 | gcry_mac_hd_t res_ctx;
9 | uint8_t *output = NULL;
10 | signal_buffer *buffer = NULL;
11 | size_t hash_len;
12 | void *context = NULL;
13 |
14 | omemo_hmac_sha256_init(&context, (uint8_t *)key, strlen(key), NULL);
15 | omemo_hmac_sha256_update(context, (uint8_t *)test, strlen(test), NULL);
16 | omemo_hmac_sha256_final(context, &buffer, NULL);
17 | omemo_hmac_sha256_cleanup(context, NULL);
18 |
19 | hash_len = gcry_mac_test_algo(GCRY_MAC_HMAC_SHA256);
20 | uint8_t result[hash_len];
21 | memset(result, 0x0, hash_len);
22 |
23 | output = signal_buffer_data(buffer);
24 |
25 | gcry_mac_open(&res_ctx, GCRY_MAC_HMAC_SHA256, 0, NULL);
26 | gcry_mac_setkey(res_ctx, key, strlen(key));
27 | gcry_mac_write(res_ctx, test, strlen(test));
28 | gcry_mac_read(res_ctx, result, &hash_len);
29 | gcry_mac_close(res_ctx);
30 |
31 | ASSERT_EQ(memcmp(result, output, hash_len), 0);
32 |
33 | signal_buffer_free(buffer);
34 | }
35 |
36 | TEST(crypto, sha512)
37 | {
38 | const char *test = "Hello, OMEMO!";
39 |
40 | uint8_t *output = NULL;
41 | uint8_t result[64];
42 | signal_buffer *buffer = NULL;;
43 | size_t buffer_len;
44 | void *context = NULL;
45 |
46 | omemo_init_provider();
47 |
48 | omemo_sha512_digest_init(&context, NULL);
49 | omemo_sha512_digest_update(context, (uint8_t *)test, strlen(test), NULL);
50 | omemo_sha512_digest_final(context, &buffer, NULL);
51 | omemo_sha512_digest_cleanup(context, NULL);
52 |
53 | memset(result, 0x0, 64);
54 |
55 | output = signal_buffer_data(buffer);
56 |
57 | gcry_md_hash_buffer(GCRY_MD_SHA512, result, test, strlen(test));
58 |
59 | ASSERT_EQ(memcmp(result, output, signal_buffer_len(buffer)), 0);
60 |
61 | signal_buffer_free(buffer);
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/test/test_omemo_device.cpp:
--------------------------------------------------------------------------------
1 | #include "test_main.h"
2 |
3 | TEST(omemo_device, creation)
4 | {
5 | const char *jid = "test@testserver.test";
6 | const uint32_t id = 1337;
7 |
8 | struct omemo_device *test_device;
9 |
10 | test_device = omemo_device_create(jid, id);
11 |
12 | ASSERT_STREQ(test_device->address.name, jid);
13 | ASSERT_EQ(test_device->address.device_id, id);
14 |
15 | omemo_device_free(test_device);
16 | }
17 |
--------------------------------------------------------------------------------
/test/test_omemo_device_list.cpp:
--------------------------------------------------------------------------------
1 | #include "test_main.h"
2 |
3 | TEST(device_list, add)
4 | {
5 | int retval;
6 | struct device_list *list = NULL;
7 |
8 | struct omemo_device *device = omemo_device_create("test", 1337);
9 |
10 | retval = omemo_device_list_add(&list, device);
11 |
12 | ASSERT_EQ(retval, 1);
13 |
14 | ASSERT_EQ(list->next, nullptr);
15 | ASSERT_EQ(list->device, device);
16 |
17 | omemo_device_list_free(&list);
18 |
19 | ASSERT_EQ(list, nullptr);
20 | }
21 |
22 | TEST(device_list, size)
23 | {
24 | int retval;
25 | struct device_list *list = NULL;
26 | struct omemo_device *st_device = omemo_device_create("test", 1337);
27 | struct omemo_device *nd_device = omemo_device_create("test", 1338);
28 |
29 | retval = omemo_device_list_size(&list);
30 |
31 | ASSERT_EQ(retval, 0);
32 |
33 | omemo_device_list_add(&list, st_device);
34 | retval = omemo_device_list_size(&list);
35 | ASSERT_EQ(retval, 1);
36 |
37 | omemo_device_list_add(&list, nd_device);
38 | retval = omemo_device_list_size(&list);
39 | ASSERT_EQ(retval, 2);
40 |
41 | omemo_device_list_free(&list);
42 | ASSERT_EQ(list, nullptr);
43 | }
44 |
45 | TEST(device_list, contains)
46 | {
47 | int retval;
48 | struct device_list *list = NULL;
49 | struct omemo_device *in_list = omemo_device_create("test", 1337);
50 | struct omemo_device *nin_list = omemo_device_create("test2", 1338);
51 |
52 | omemo_device_list_add(&list, in_list);
53 |
54 | retval = omemo_device_list_contains(&list, in_list);
55 |
56 | ASSERT_EQ(retval, 1);
57 |
58 | retval = omemo_device_list_contains(&list, nin_list);
59 |
60 | ASSERT_EQ(retval, 0);
61 |
62 | omemo_device_list_free(&list);
63 | omemo_device_free(nin_list);
64 | }
65 |
66 | TEST(device_list, contains_id)
67 | {
68 |
69 | int retval;
70 | struct device_list *list = NULL;
71 | struct omemo_device *in_list = omemo_device_create("test", 1337);
72 | struct omemo_device *nin_list = omemo_device_create("test2", 1338);
73 |
74 | omemo_device_list_add(&list, in_list);
75 |
76 | retval = omemo_device_list_contains_id(&list, 1337);
77 |
78 | ASSERT_EQ(retval, 1);
79 |
80 | retval = omemo_device_list_contains_id(&list, 1338);
81 |
82 | ASSERT_EQ(retval, 0);
83 |
84 | omemo_device_list_free(&list);
85 | omemo_device_free(nin_list);
86 | }
87 |
88 | TEST(device_list, remove)
89 | {
90 | int retval;
91 | struct device_list *list = NULL;
92 |
93 | struct omemo_device *in_list = omemo_device_create("test", 1337);
94 |
95 | omemo_device_list_add(&list, in_list);
96 |
97 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 1);
98 | ASSERT_EQ(omemo_device_list_size(&list), 1);
99 |
100 | omemo_device_list_remove(&list, in_list);
101 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 0);
102 | ASSERT_EQ(omemo_device_list_size(&list), 0);
103 |
104 | omemo_device_free(in_list);
105 | omemo_device_list_free(&list);
106 | }
107 |
108 | TEST(device_list, remove_free_device)
109 | {
110 | int retval;
111 | struct device_list *list = NULL;
112 |
113 | struct omemo_device *in_list = omemo_device_create("test", 1337);
114 |
115 | omemo_device_list_add(&list, in_list);
116 |
117 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 1);
118 | ASSERT_EQ(omemo_device_list_size(&list), 1);
119 |
120 | omemo_device_list_free_device(&list, in_list);
121 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 0);
122 | ASSERT_EQ(omemo_device_list_size(&list), 0);
123 |
124 | omemo_device_list_free(&list);
125 | }
126 |
127 | TEST(device_list, free)
128 | {
129 | struct device_list *list = NULL;
130 | struct omemo_device *in_list = omemo_device_create("test", 1337);
131 |
132 | omemo_device_list_add(&list, in_list);
133 |
134 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 1);
135 |
136 | omemo_device_list_free(&list);
137 | ASSERT_EQ(omemo_device_list_contains(&list, in_list), 0);
138 | ASSERT_EQ(list, nullptr);
139 | }
140 |
141 | TEST(device_list, inval_input)
142 | {
143 | struct device_list *list = NULL;
144 | struct omemo_device *in_list = omemo_device_create("test", 1337);
145 |
146 | ASSERT_EQ(omemo_device_list_add(NULL, in_list), -1);
147 | ASSERT_EQ(omemo_device_list_add(&list, NULL), -1);
148 |
149 | ASSERT_EQ(omemo_device_list_contains(NULL, in_list), -1);
150 | ASSERT_EQ(omemo_device_list_contains(&list, NULL), -1);
151 |
152 | ASSERT_EQ(omemo_device_list_contains_id(NULL, 1), -1);
153 | ASSERT_EQ(omemo_device_list_contains_id(&list, 0), -1);
154 |
155 | ASSERT_EQ(omemo_device_list_remove(NULL, in_list), -1);
156 | ASSERT_EQ(omemo_device_list_remove(&list, NULL), -1);
157 |
158 | ASSERT_EQ(omemo_device_list_size(NULL), 0);
159 |
160 | ASSERT_EQ(omemo_device_list_free_device(NULL, in_list), -1);
161 | ASSERT_EQ(omemo_device_list_free_device(&list, NULL), -1);
162 |
163 | omemo_device_list_free(&list);
164 | omemo_device_free(in_list);
165 | }
166 |
--------------------------------------------------------------------------------
/test/test_omemo_pubsub.cpp:
--------------------------------------------------------------------------------
1 | #include "test_main.h"
2 |
3 | TEST(pubsub, xml_output)
4 | {
5 | struct device_list *list = NULL;
6 | struct omemo_device *device = NULL;
7 | struct omemo_device *inactive = NULL;
8 |
9 | device = omemo_device_create("test@test.test", 1337);
10 | inactive = omemo_device_create("test@test.test", 1000);
11 | inactive->status = INACTIVE;
12 |
13 | omemo_device_list_add(&list, device);
14 | omemo_device_list_add(&list, inactive);
15 |
16 | omemo_publish_device_list("test@test.test", &list);
17 |
18 | omemo_device_list_free(&list);
19 | }
20 |
--------------------------------------------------------------------------------
/test/test_omemo_store.cpp:
--------------------------------------------------------------------------------
1 | #include "test_main.h"
2 |
3 | TEST(store, store_user_device_list)
4 | {
5 | signal_protocol_address nick = {"test", 4, 1337};
6 | struct device_list *list = NULL;
7 | struct omemo_device *device = NULL;
8 | struct omemo_device *inactive = NULL;
9 |
10 | device = omemo_device_create("test@test.test", 1337);
11 | inactive = omemo_device_create("test@test.test", 1000);
12 | inactive->status = INACTIVE;
13 |
14 | omemo_device_list_add(&list, device);
15 | omemo_device_list_add(&list, inactive);
16 |
17 | std::string result("A:1337\nI:1000");
18 | std::string dir(std::getenv("HOME"));
19 | dir += std::string("/") + std::string(OMEMO_WORKING_DIR);
20 | dir += std::string("/") + std::string(nick.name) + std::string("/");
21 | dir += std::to_string(nick.device_id);
22 | dir += "/devices";
23 |
24 | ASSERT_TRUE(!omemo_store_device_list(&nick, list));
25 | std::fstream f(dir.c_str());
26 | ASSERT_TRUE(f.good());
27 |
28 | std::string content((std::istreambuf_iterator(f)),
29 | std::istreambuf_iterator());
30 | ASSERT_TRUE(content == result);
31 |
32 | omemo_device_list_free(&list);
33 | }
34 |
35 | TEST(store, store_contacts_device_list)
36 | {
37 | signal_protocol_address nick = {"test", 4, 1337};
38 | struct device_list *list = NULL;
39 | struct omemo_device *device = NULL;
40 | struct omemo_device *inactive = NULL;
41 |
42 | device = omemo_device_create("notTest@test.test", 1337);
43 | inactive = omemo_device_create("notTest@test.test", 1000);
44 | inactive->status = INACTIVE;
45 |
46 | omemo_device_list_add(&list, device);
47 | omemo_device_list_add(&list, inactive);
48 |
49 | std::string result("A:1337\nI:1000");
50 | std::string dir(std::getenv("HOME"));
51 | dir += std::string("/") + std::string(OMEMO_WORKING_DIR);
52 | dir += std::string("/") + std::string(nick.name) + std::string("/");
53 | dir += std::to_string(nick.device_id);
54 | dir += "/contacts/notTest@test.test"; dir += "/devices";
55 |
56 |
57 | ASSERT_TRUE(!omemo_store_device_list(&nick, list));
58 | std::fstream f(dir.c_str());
59 | ASSERT_TRUE(f.good());
60 |
61 | std::string content((std::istreambuf_iterator(f)),
62 | std::istreambuf_iterator());
63 | ASSERT_TRUE(content == result);
64 |
65 | omemo_device_list_free(&list);
66 | }
67 |
68 | TEST(store, is_local_user_existant)
69 | {
70 | signal_protocol_address nick = {"test", 4, 1337};
71 | signal_protocol_address false_nick = {"notTest", 7, 1000};
72 | struct device_list *list = NULL;
73 | struct omemo_device *device = NULL;
74 |
75 | device = omemo_device_create("test@test.test", 1337);
76 |
77 | omemo_device_list_add(&list, device);
78 |
79 | ASSERT_TRUE(!omemo_store_device_list(&nick, list));
80 | ASSERT_TRUE(omemo_is_local_user_existent(&nick) == 1);
81 | ASSERT_TRUE(omemo_is_local_user_existent(&false_nick) == 0);
82 |
83 | omemo_device_list_free(&list);
84 | }
85 |
--------------------------------------------------------------------------------
/xmpp/omemo_constants.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_OMEMO_CONST_H
2 | #define PROF_OMEMO_OMEMO_CONST_H
3 |
4 | #include
5 |
6 | static const char PUBSUB_PROTO_XML_NS[] = "http://jabber.org/protocol/pubsub";
7 |
8 | static const char OMEMO_WORKING_DIR[] = ".local/share/profanity/omemo";
9 |
10 | static const char OMEMO_DEVICE_LIST_PUBLISH_XML_NODE[] = "urn:xmpp:omemo:0:devicelist";
11 | static const char OMEMO_DEVICE_LIST_XML_NS[] = "urn:xmpp:omemo:0";
12 |
13 | static const uint32_t OMEMO_NUM_PRE_KEYS = 100;
14 |
15 | #endif /* PROF_OMEMO_OMEMO_CONST_H */
16 |
--------------------------------------------------------------------------------
/xmpp/pubsub.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "pubsub.h"
8 |
9 | int omemo_publish_device_list(const char *jid, struct device_list **list)
10 | {
11 | int buf_size = 0;
12 | xmlBufferPtr buf = NULL;
13 | xmlNodePtr root, pubsub, publish, item;
14 | xmlNsPtr pubsub_ns;
15 |
16 | pubsub_ns = xmlNewNs(NULL, BAD_CAST PUBSUB_PROTO_XML_NS, NULL);
17 | if (!pubsub_ns) {
18 | errno = EINVAL;
19 | return -1;
20 | }
21 |
22 | root = xmlNewNode(NULL, BAD_CAST "iq");
23 | xmlNewProp(root, BAD_CAST "from", BAD_CAST jid );
24 | xmlNewProp(root, BAD_CAST "type", BAD_CAST "set" );
25 | xmlNewProp(root, BAD_CAST "id", BAD_CAST "announce1");
26 |
27 | pubsub = xmlNewNode(pubsub_ns, BAD_CAST "pubsub");
28 | xmlAddChild(root, pubsub);
29 |
30 | publish = xmlNewNode(NULL, BAD_CAST "publish");
31 | xmlNewProp(publish, BAD_CAST "node", BAD_CAST OMEMO_DEVICE_LIST_PUBLISH_XML_NODE);
32 | xmlAddChild(pubsub, publish);
33 |
34 | item = xmlNewNode(NULL, BAD_CAST "item");
35 | xmlAddChild(publish, item);
36 |
37 | if (omemo_device_list_serialize_xml(&item, list) < 0) {
38 | return -1;
39 | }
40 |
41 | /* Now Dump everything in the buffer and copy it into string to send */
42 | buf = xmlBufferCreate();
43 | buf_size = xmlNodeDump(buf, NULL, root, 0, 1);
44 | if (buf_size < 0) {
45 | errno = EIO;
46 | return -1;
47 | }
48 |
49 |
50 | /* Send the stanza here */
51 | puts((char *)buf->content);
52 |
53 | xmlBufferFree(buf);
54 | xmlFreeNode(root);
55 | xmlFreeNs(pubsub_ns);
56 |
57 | xmlCleanupParser();
58 |
59 | return 0;
60 | }
61 |
62 |
63 |
--------------------------------------------------------------------------------
/xmpp/pubsub.h:
--------------------------------------------------------------------------------
1 | #ifndef PROF_OMEMO_PUBSUB_H
2 | #define PROF_OMEMO_PUBSUB_H
3 |
4 | #include
5 |
6 | #ifdef __cplusplus
7 | extern "C" {
8 | #endif
9 |
10 | struct device_list;
11 |
12 | int omemo_publish_device_list(const char *jid, struct device_list **list);
13 |
14 | #ifdef __cplusplus
15 | }
16 | #endif
17 |
18 | #endif /* PROF_OMEMO_PUBSUB_H */
19 |
--------------------------------------------------------------------------------
/xmpp/targets.cmake:
--------------------------------------------------------------------------------
1 | set (
2 | XMPP_SRC
3 | xmpp/pubsub.h
4 | xmpp/pubsub.c
5 | xmpp/omemo_constants.h
6 | )
7 |
--------------------------------------------------------------------------------