pax_global_header00006660000000000000000000000064136414450140014514gustar00rootroot0000000000000052 comment=9e63125e8715c6198ca1185d4e0acb6e1ab0c7ff bzrtp-4.4.13/000077500000000000000000000000001364144501400127465ustar00rootroot00000000000000bzrtp-4.4.13/.gitignore000066400000000000000000000005001364144501400147310ustar00rootroot00000000000000Makefile Makefile.in aclocal.m4 autom4te.cache compile config.guess config.h config.h.in config.log config.status config.sub configure depcomp install-sh intltool-extract intltool-merge intltool-update libtool ltmain.sh missing mkinstalldirs stamp-h1 *.o *.exe *.zip *~ *.lo *.la *.swp .deps .libs INSTALL m4 bzrtp.spec bzrtp-4.4.13/Android.mk000066400000000000000000000014351364144501400146620ustar00rootroot00000000000000LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS += -DHAVE_LIBXML2 LOCAL_MODULE := libbzrtp LOCAL_ARM_MODE := arm LOCAL_SRC_FILES = \ src/bzrtp.c \ src/cryptoUtils.c \ src/packetParser.c \ src/stateMachine.c \ src/zidCache.c \ src/pgpwords.c LOCAL_STATIC_LIBRARIES += liblpxml2 LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/include \ $(LOCAL_PATH)/../externals/libxml2/include \ $(LOCAL_PATH)/../externals/build/libxml2 ifeq ($(BUILD_BCTOOLBOX_MBEDTLS),1) LOCAL_SRC_FILES += src/cryptoMbedtls.c LOCAL_STATIC_LIBRARIES += mbedtls LOCAL_C_INCLUDES += $(LOCAL_PATH)/../externals/mbedtls/include else LOCAL_SRC_FILES += src/cryptoPolarssl.c LOCAL_STATIC_LIBRARIES += polarssl LOCAL_C_INCLUDES += $(LOCAL_PATH)/../externals/polarssl/include endif include $(BUILD_STATIC_LIBRARY) bzrtp-4.4.13/CHANGELOG.md000066400000000000000000000005371364144501400145640ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.0.0] - 2015-03-15 ### Added - Initial release. ### Changed ### Removed bzrtp-4.4.13/CMakeLists.txt000066400000000000000000000112521364144501400155070ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2010-2019 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ cmake_minimum_required(VERSION 3.1) project(bzrtp VERSION 4.4.0 LANGUAGES C CXX) option(ENABLE_SHARED "Build shared library." YES) option(ENABLE_STATIC "Build static library." YES) option(ENABLE_ZIDCACHE "Turn on compilation of ZID cache, request sqlite" YES) option(ENABLE_STRICT "Build with strict compile options." YES) option(ENABLE_TESTS "Enable compilation of unit tests." NO) option(ENABLE_EXPORTEDKEY_V1_0_RETROCOMPATIBILITY "Enable support for Limev1 with older version of bzrtp(before v1.06)" YES) option(ENABLE_PACKAGE_SOURCE "Create 'package_source' target for source archive making (CMake >= 3.11)" OFF) if(NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX) set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}) message(STATUS "Setting install rpath to ${CMAKE_INSTALL_RPATH}") endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(MSVC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/MSVC") if(MSVC) list(APPEND CMAKE_REQUIRED_INCLUDES ${MSVC_INCLUDE_DIR}) endif() include(GNUInstallDirs) include(CheckLibraryExists) check_library_exists("m" "sqrt" "" HAVE_SQRT) find_package(bctoolbox 0.0.3 REQUIRED OPTIONAL_COMPONENTS tester CONFIG) if(ENABLE_ZIDCACHE) find_package(Sqlite3 REQUIRED) # Also check if we have libxml2, as we need it for migration purpose find_package(XML2) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/config.h PROPERTIES GENERATED ON) add_definitions("-DHAVE_CONFIG_H") set(STRICT_OPTIONS_CPP ) if(NOT MSVC) list(APPEND STRICT_OPTIONS_CPP "-Wall") if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND STRICT_OPTIONS_CPP "-Qunused-arguments") endif() if(ENABLE_STRICT) list(APPEND STRICT_OPTIONS_CPP "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-missing-field-initializers") endif() endif() if(STRICT_OPTIONS_CPP) list(REMOVE_DUPLICATES STRICT_OPTIONS_CPP) string(REPLACE ";" " " STRICT_OPTIONS_CPP "${STRICT_OPTIONS_CPP}") endif() set(BZRTP_CPPFLAGS ${BCTOOLBOX_CPPFLAGS}) if(ENABLE_STATIC) list(APPEND BZRTP_CPPFLAGS "-DBZRTP_STATIC") endif() if(BZRTP_CPPFLAGS) list(REMOVE_DUPLICATES BZRTP_CPPFLAGS) add_definitions(${BZRTP_CPPFLAGS}) endif() include_directories( include ${CMAKE_CURRENT_BINARY_DIR} ) if(MSVC) include_directories(${MSVC_INCLUDE_DIR}) endif() if(ENABLE_ZIDCACHE) add_definitions("-DZIDCACHE_ENABLED") if(XML2_FOUND) add_definitions("-DHAVE_LIBXML2") endif() endif() if (ENABLE_EXPORTEDKEY_V1_0_RETROCOMPATIBILITY) add_definitions("-DSUPPORT_EXPORTEDKEY_V010000") endif() set(EXPORT_TARGETS_NAME "bzrtp") add_subdirectory(include) add_subdirectory(src) if(ENABLE_TESTS) enable_testing() add_subdirectory(test) endif() include(CMakePackageConfigHelpers) set(CONFIG_PACKAGE_LOCATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake") write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}ConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) export(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}Targets.cmake" ) configure_package_config_file(cmake/BZRTPConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}Config.cmake" INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION} NO_SET_AND_CHECK_MACRO ) install(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE ${EXPORT_TARGETS_NAME}Targets.cmake DESTINATION ${CONFIG_PACKAGE_LOCATION} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}ConfigVersion.cmake" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) if (ENABLE_PACKAGE_SOURCE) add_subdirectory(build) endif() bzrtp-4.4.13/LICENSE.txt000066400000000000000000000773311364144501400146040ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS bzrtp-4.4.13/Makefile.am000066400000000000000000000005211364144501400150000ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src include test EXTRA_DIST= \ CMakeLists.txt \ cmake/BZRTPConfig.cmake.in \ cmake/FindXML2.cmake \ config.h.cmake \ include/CMakeLists.txt \ src/CMakeLists.txt \ test/CMakeLists.txt \ README.md test: cd test && $(MAKE) test pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libbzrtp.pc bzrtp-4.4.13/README.md000066400000000000000000000056111364144501400142300ustar00rootroot00000000000000[![pipeline status](https://gitlab.linphone.org/BC/public/bzrtp/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/bzrtp/commits/master) BZRTP ===== What's BZRTP ------------ BZRTP is an opensource implementation of ZRTP keys exchange protocol. The library written in C 89 is fully portable and can be executed on many platforms including both ARM processor and x86. Licensing: The source code is licensed under GPLv2. License -------- BZRTP library is dual-licensed and can be distributed either under a GNU GPLv3 license (open source, see *LICENSE.txt*) or under a proprietary license (closed source). Copyright © Belledonne Communications SARL Johan Pascal is the original author of BZRTP. Compatibility with RFC6189 - ZRTP: Media Path Key Agreement for Unicast Secure RTP ---------------------------------------------------------------------------------- ### Mandatory but NOT implemented * Sas Relay mechanism (section 7.3) * Error message generation, emission or reception(which doesn't imply any security problem, they are mostly for debug purpose) ### Optional and implementd * multistream mode * cacheless implementation * zrtp-hash attribute in SDP ### Optional and NOT implemented * Go Clear/Clear ACK messages * SAS signing ### Supported Algorithms * Hash : SHA-256, SHA-384 * Cipher : AES-128, AES-256 * SAS rendering: B32, B256(PGP word list) * Auth Tag : HS32, HS80 * Key Agreement : DH-2048, DH-3072, X25519, X448 *Note*: X25519 and X448 Key agreements(RFC7748) are not part of RFC6189 and supported only when *bctoolbox[1]* is linking *libdecaf[2]* Dependencies ------------ - *bctoolbox[1]*: portability layer and crypto function abstraction Build BZRTP ----------- cmake . -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= make make install Build options ------------- * `CMAKE_INSTALL_PREFIX=` : install prefix * `CMAKE_PREFIX_PATH=` : column-separated list of prefixes where to search for dependencies * `ENABLE_SHARED=NO` : do not build the shared library * `ENABLE_STATIC=NO` : do not build the static library * `ENABLE_STRICT=NO` : build without the strict compilation flags * `ENABLE_TESTS=YES` : build non-regression tests Notes for packagers ------------------- Our CMake scripts may automatically add some paths into research paths of generated binaries. To ensure that the installed binaries are striped of any rpath, use `-DCMAKE_SKIP_INSTALL_RPATH=ON` while you invoke cmake. Rpm packaging bzrtp can be generated with cmake3 using the following command: mkdir WORK cd WORK cmake3 ../ make package_source rpmbuild -ta --clean --rmsource --rmspec bzrtp--.tar.gz ---------------------------------- * [1] git://git.linphone.org/bctoolbox.git or * [2] bzrtp-4.4.13/autogen.sh000077500000000000000000000027301364144501400147510ustar00rootroot00000000000000#!/bin/sh ## ## Copyright (c) 2014-2019 Belledonne Communications SARL. ## ## This file is part of bzrtp. ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## srcdir=`dirname $0` test -z "$srcdir" && srcdir=. THEDIR=`pwd` cd $srcdir #AM_VERSION="1.11" if ! type aclocal-$AM_VERSION 1>/dev/null 2>&1; then AUTOMAKE=automake ACLOCAL=aclocal else ACLOCAL=aclocal-${AM_VERSION} AUTOMAKE=automake-${AM_VERSION} fi if test -f /opt/local/bin/glibtoolize ; then # darwin LIBTOOLIZE=/opt/local/bin/glibtoolize else LIBTOOLIZE=libtoolize fi if test -d /opt/local/share/aclocal ; then ACLOCAL_ARGS="-I /opt/local/share/aclocal" fi if test -d /share/aclocal ; then ACLOCAL_ARGS="-I /share/aclocal" fi echo "Generating build scripts for BZRtp: ZRTP engine" set -x $LIBTOOLIZE --copy --force $ACLOCAL $ACLOCAL_ARGS #autoheader $AUTOMAKE --force-missing --add-missing --copy autoconf cd $THEDIR bzrtp-4.4.13/build/000077500000000000000000000000001364144501400140455ustar00rootroot00000000000000bzrtp-4.4.13/build/CMakeLists.txt000066400000000000000000000024651364144501400166140ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2010-2019 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ cmake_minimum_required(VERSION 3.11) # we need CMake 3.11 for defining 'package_source' target as custom target if(NOT CPACK_PACKAGE_NAME) set(CPACK_PACKAGE_NAME "bzrtp") ENDIF() set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}" "^${PROJECT_SOURCE_DIR}/.git*" ) bc_make_package_source_target() bzrtp-4.4.13/build/rpm/000077500000000000000000000000001364144501400146435ustar00rootroot00000000000000bzrtp-4.4.13/build/rpm/bzrtp.spec.cmake000077500000000000000000000055371364144501400177540ustar00rootroot00000000000000# -*- rpm-spec -*- %define _prefix @CMAKE_INSTALL_PREFIX@ %define pkg_prefix @BC_PACKAGE_NAME_PREFIX@ %define package_name @CPACK_PACKAGE_NAME@-${FULL_VERSION} # re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir # taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros %define _datarootdir %{_prefix}/share %define _datadir %{_datarootdir} %define _docdir %{_datadir}/doc Name: @CPACK_PACKAGE_NAME@ Version: ${RPM_VERSION} Release: ${RPM_RELEASE}%{?dist} Summary: BZRTP is an opensource implementation of ZRTP keys exchange protocol. Group: Applications/Communications License: GPL URL: http://www.linphone.org Source0: %{package_name}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Requires: %{pkg_prefix}bctoolbox %description BZRTP is an opensource implementation of ZRTP keys exchange protocol. The library written in C 89 is fully portable and can be executed on many platforms including both ARM processor and x86. Licensing: The source code is licensed under GPLv2. %package devel Summary: Development libraries for bzrtp Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Libraries and headers required to develop software with bzrtp %if 0%{?rhel} && 0%{?rhel} <= 7 %global cmake_name cmake3 %define ctest_name ctest3 %else %global cmake_name cmake %define ctest_name ctest %endif # This is for debian builds where debug_package has to be manually specified, whereas in centos it does not %define custom_debug_package %{!?_enable_debug_packages:%debug_package}%{?_enable_debug_package:%{nil}} %custom_debug_package %prep %setup -n %{package_name} %build %{expand:%%%cmake_name} . -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ -DCMAKE_PREFIX_PATH:PATH=%{_prefix} @RPM_ALL_CMAKE_OPTIONS@ make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} # Dirty workaround to give exec rights for all shared libraries. Debian packaging needs this # TODO : set CMAKE_INSTALL_SO_NO_EXE for a cleaner workaround chmod +x `find %{buildroot} *.so.*` %check %{ctest_name} -V %{?_smp_mflags} %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root) %doc CHANGELOG.md LICENSE.txt README.md %{_libdir}/*.so.* %files devel %defattr(-,root,root) %{_includedir}/bzrtp %if @ENABLE_STATIC@ %{_libdir}/libbzrtp.a %endif %if @ENABLE_SHARED@ %{_libdir}/libbzrtp.so %endif %{_datadir}/bzrtp/cmake/BZRTPConfig*.cmake %{_datadir}/bzrtp/cmake/BZRTPTargets*.cmake %changelog * Tue Nov 27 2018 ronan.abhamon - Do not set CMAKE_INSTALL_LIBDIR and never with _libdir! * Wed Jul 19 2017 jehan.monnier - Initial RPM release. bzrtp-4.4.13/cmake/000077500000000000000000000000001364144501400140265ustar00rootroot00000000000000bzrtp-4.4.13/cmake/BZRTPConfig.cmake.in000066400000000000000000000036271364144501400174740ustar00rootroot00000000000000############################################################################ # BZRTPConfig.cmake # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # Config file for the bzrtp package. # It defines the following variables: # # BZRTP_FOUND - system has bzrtp # BZRTP_INCLUDE_DIRS - the bzrtp include directory # BZRTP_LIBRARIES - The libraries needed to use bzrtp # BZRTP_CPPFLAGS - The compilation flags needed to use bzrtp @PACKAGE_INIT@ set(BZRTP_TARGETNAME bzrtp) include("${CMAKE_CURRENT_LIST_DIR}/${BZRTP_TARGETNAME}Targets.cmake") if(@ENABLE_SHARED@) set(BZRTP_LIBRARIES ${BZRTP_TARGETNAME}) else() if(TARGET ${BZRTP_TARGETNAME}) get_target_property(BZRTP_LIBRARIES ${BZRTP_TARGETNAME} LOCATION) get_target_property(BZRTP_LINK_LIBRARIES ${BZRTP_TARGETNAME} INTERFACE_LINK_LIBRARIES) if(BZRTP_LINK_LIBRARIES) list(APPEND BZRTP_LIBRARIES ${BZRTP_LINK_LIBRARIES}) endif() endif() endif() get_target_property(BZRTP_INCLUDE_DIRS ${BZRTP_TARGETNAME} INTERFACE_INCLUDE_DIRECTORIES) set(BZRTP_CPPFLAGS @BZRTP_CPPFLAGS@) set(BZRTP_FOUND 1) bzrtp-4.4.13/cmake/FindSqlite3.cmake000066400000000000000000000034531364144501400171620ustar00rootroot00000000000000############################################################################ # FindSqlite3.cmake # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the sqlite3 include file and library # # SQLITE3_FOUND - system has sqlite3 # SQLITE3_INCLUDE_DIRS - the sqlite3 include directory # SQLITE3_LIBRARIES - The libraries needed to use sqlite3 if(APPLE AND NOT IOS) set(SQLITE3_HINTS "/usr") endif() if(SQLITE3_HINTS) set(SQLITE3_LIBRARIES_HINTS "${SQLITE3_HINTS}/lib") endif() find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h HINTS "${SQLITE3_HINTS}" PATH_SUFFIXES include ) if(SQLITE3_INCLUDE_DIRS) set(HAVE_SQLITE3_H 1) endif() find_library(SQLITE3_LIBRARIES NAMES sqlite3 HINTS "${SQLITE3_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Sqlite3 DEFAULT_MSG SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H ) mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H) bzrtp-4.4.13/cmake/FindXML2.cmake000066400000000000000000000033541364144501400163600ustar00rootroot00000000000000############################################################################ # FindXML2.txt # Copyright (C) 2015 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the libxml2 include file and library # # XML2_FOUND - system has libxml2 # XML2_INCLUDE_DIRS - the libxml2 include directory # XML2_LIBRARIES - The libraries needed to use libxml2 if(APPLE AND NOT IOS) set(XML2_HINTS "/usr") endif() if(XML2_HINTS) set(XML2_LIBRARIES_HINTS "${XML2_HINTS}/lib") endif() find_path(XML2_INCLUDE_DIRS NAMES libxml/xmlreader.h HINTS "${XML2_HINTS}" PATH_SUFFIXES include/libxml2 ) if(XML2_INCLUDE_DIRS) set(HAVE_LIBXML_XMLREADER_H 1) endif() find_library(XML2_LIBRARIES NAMES xml2 HINTS "${XML2_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(XML2 DEFAULT_MSG XML2_INCLUDE_DIRS XML2_LIBRARIES ) mark_as_advanced(XML2_INCLUDE_DIRS XML2_LIBRARIES) bzrtp-4.4.13/config.h.cmake000066400000000000000000000022261364144501400154450ustar00rootroot00000000000000/*************************************************************************** * config.h.cmake * Copyright (C) 2014 Belledonne Communications, Grenoble France * **************************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ****************************************************************************/ #cmakedefine HAVE_CU_ADD_SUITE #cmakedefine HAVE_CU_GET_SUITE #cmakedefine HAVE_CU_CURSES #cmakedefine ZIDCACHE_ENABLED #cmakedefine HAVE_LIBXML2 bzrtp-4.4.13/configure.ac000066400000000000000000000047501364144501400152420ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_INIT([bzrtp],[1.0.5]) AC_PREREQ(2.63) AC_CONFIG_SRCDIR([src/bzrtp.c]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([tar-ustar --warnings=no-portability foreign]) AC_PROG_CC(["xcrun clang" gcc]) LT_INIT(win32-dll shared disable-static) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_ARG_ENABLE(strict, [ --enable-strict Enable error on compilation warning [default=yes]], [wall_werror=$enableval], [wall_werror=yes] ) # configure option to disable the tests PKG_CHECK_MODULES(BCTOOLBOXTESTER, bctoolbox-tester, [found_pkg_config_bctoolboxtester=yes],[found_pkg_config_bctoolboxtester=no]) if test "$found_pkg_config_bctoolboxtester" = "no" ; then AC_MSG_WARN([Could not find bctoolbox-tester wrapper, tests are not compiled.]) fi dnl check bctoolbox PKG_CHECK_MODULES(BCTOOLBOX, [bctoolbox] ,[bctoolbox_found=yes] ,foo=bar) if test "$bctoolbox_found" != "yes" ; then AC_MSG_ERROR([bctoolbox not found, aborting. ]) fi dnl check for sqlite PKG_CHECK_MODULES(SQLITE3,[sqlite3 >= 3.6.0],[found_sqlite=yes],[found_sqlite=no]) if test "$found_sqlite" != "yes" ; then AC_MSG_WARN([sqlite3 not found. Disabling cache.]) else AC_DEFINE(ZIDCACHE_ENABLED,1,[defined when libxml2 is available]) fi dnl check libxml2 PKG_CHECK_MODULES(LIBXML2, [libxml-2.0] ,[libxml2_found=yes] ,foo=bar) if test "$libxml2_found$found_sqlite" != "yesyes" ; then AC_MSG_WARN([libxml2 not found. Disabling cache.]) else AC_DEFINE(HAVE_LIBXML2,1,[defined when libxml2 is available]) fi AC_ARG_ENABLE(tests, [AS_HELP_STRING([--disable-tests], [Disable compilation of tests])], [case "${enableval}" in yes) tests_enabled=true ;; no) tests_enabled=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --disable-tests) ;; esac], [tests_enabled=yes] ) AM_CONDITIONAL(ENABLE_TESTS, test x$tests_enabled = xyes && test x$found_pkg_config_bctoolboxtester = xyes) CFLAGS="$CFLAGS -Wall" case $CC in *clang*) CFLAGS="$CFLAGS -Qunused-arguments" ;; esac if test $GCC = yes && test $wall_werror = yes; then CFLAGS="$CFLAGS -Werror -Wextra -Wno-unused-parameter -Wno-missing-field-initializers " fi # Create the following files from their .in counterparts AC_CONFIG_FILES([ Makefile src/Makefile include/Makefile include/bzrtp/Makefile test/Makefile libbzrtp.pc ]) AC_OUTPUT bzrtp-4.4.13/include/000077500000000000000000000000001364144501400143715ustar00rootroot00000000000000bzrtp-4.4.13/include/CMakeLists.txt000066400000000000000000000022111364144501400171250ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ install(FILES bzrtp/bzrtp.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/bzrtp PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) bzrtp-4.4.13/include/MSVC/000077500000000000000000000000001364144501400151415ustar00rootroot00000000000000bzrtp-4.4.13/include/MSVC/stdint.h000066400000000000000000000170601364144501400166230ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] bzrtp-4.4.13/include/Makefile.am000066400000000000000000000001361364144501400164250ustar00rootroot00000000000000SUBDIRS = bzrtp EXTRA_DIST=cryptoUtils.h packetParser.h stateMachine.h typedef.h zidCache.h bzrtp-4.4.13/include/bzrtp/000077500000000000000000000000001364144501400155325ustar00rootroot00000000000000bzrtp-4.4.13/include/bzrtp/Makefile.am000066400000000000000000000001521364144501400175640ustar00rootroot00000000000000bzrtp_includedir=$(includedir)/bzrtp bzrtp_include_HEADERS= bzrtp.h EXTRA_DIST=$(bzrtp_include_HEADERS) bzrtp-4.4.13/include/bzrtp/bzrtp.h000066400000000000000000000774231364144501400170610ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BZRTP_H #define BZRTP_H #include #include "bctoolbox/crypto.h" #include "bctoolbox/port.h" #ifdef _MSC_VER #ifdef BZRTP_STATIC #define BZRTP_EXPORT #else /* BZRTP_STATIC */ #ifdef BZRTP_EXPORTS #define BZRTP_EXPORT __declspec(dllexport) #else /* BZRTP_EXPORTS */ #define BZRTP_EXPORT __declspec(dllimport) #endif /* BZRTP_EXPORTS */ #endif /* BZRTP_STATIC */ #ifndef BZRTP_DEPRECATED #define BZRTP_DEPRECATED __declspec(deprecated) #endif /* BZRTP_DEPRECATED */ #else /* _MSC_VER*/ #define BZRTP_EXPORT __attribute__ ((visibility ("default"))) #ifndef BZRTP_DEPRECATED #define BZRTP_DEPRECATED __attribute__ ((deprecated)) #endif /* BZRTP_DEPRECATED */ #endif /* _MSC_VER*/ /** * Define different types of crypto functions */ #define ZRTP_HASH_TYPE 0x01 #define ZRTP_CIPHERBLOCK_TYPE 0x02 #define ZRTP_AUTHTAG_TYPE 0x04 #define ZRTP_KEYAGREEMENT_TYPE 0x08 #define ZRTP_SAS_TYPE 0x10 /** * map the differents algorithm (some may not be available) to integer */ #define ZRTP_UNSET_ALGO 0x00 #define ZRTP_HASH_S256 0x11 #define ZRTP_HASH_S384 0x12 #define ZRTP_HASH_N256 0x13 #define ZRTP_HASH_N384 0x14 #define ZRTP_CIPHER_AES1 0x21 #define ZRTP_CIPHER_AES2 0x22 #define ZRTP_CIPHER_AES3 0x23 #define ZRTP_CIPHER_2FS1 0x24 #define ZRTP_CIPHER_2FS2 0x25 #define ZRTP_CIPHER_2FS3 0x26 #define ZRTP_AUTHTAG_HS32 0x31 #define ZRTP_AUTHTAG_HS80 0x32 #define ZRTP_AUTHTAG_SK32 0x33 #define ZRTP_AUTHTAG_SK64 0x34 /** * WARNING : it is very important to keep the key agreement defined in that order * as it is used to easily sort them from faster(DH2k) to slower(EC52) */ #define ZRTP_KEYAGREEMENT_DH2k 0x41 #define ZRTP_KEYAGREEMENT_X255 0x42 #define ZRTP_KEYAGREEMENT_EC25 0x43 #define ZRTP_KEYAGREEMENT_X448 0x44 #define ZRTP_KEYAGREEMENT_DH3k 0x45 #define ZRTP_KEYAGREEMENT_EC38 0x46 #define ZRTP_KEYAGREEMENT_EC52 0x47 #define ZRTP_KEYAGREEMENT_Prsh 0x4e #define ZRTP_KEYAGREEMENT_Mult 0x4f #define ZRTP_SAS_B32 0x51 #define ZRTP_SAS_B256 0x52 /** * Define to give client indication on which srtp secrets are valid when given */ #define ZRTP_SRTP_SECRETS_FOR_SENDER 0x01 #define ZRTP_SRTP_SECRETS_FOR_RECEIVER 0x02 /** * brief The data structure containing the keys and algorithms to be used by srtp * Also stores SAS and informations about the crypto algorithms selected during ZRTP negotiation */ typedef struct bzrtpSrtpSecrets_struct { uint8_t *selfSrtpKey; /**< The key used by local part to encrypt */ uint8_t selfSrtpKeyLength; /**< The length in byte of the key */ uint8_t *selfSrtpSalt; /**< The salt used by local part to encrypt */ uint8_t selfSrtpSaltLength; /**< The length in byte of the salt */ uint8_t *peerSrtpKey; /**< The key used by local part to decrypt */ uint8_t peerSrtpKeyLength; /**< The length in byte of the key */ uint8_t *peerSrtpSalt; /**< The salt used by local part to decrypt */ uint8_t peerSrtpSaltLength; /**< The length in byte of the salt */ uint8_t cipherAlgo; /**< The cipher block algorithm selected durign ZRTP negotiation and used by srtp */ uint8_t cipherKeyLength; /**< The key length in bytes for the cipher block algorithm used by srtp */ uint8_t authTagAlgo; /**< srtp authentication tag algorithm agreed on after Hello packet exchange */ char *sas; /**< a null terminated char containing the Short Authentication String */ uint8_t sasLength; /**< The length of sas, including the termination character */ uint8_t hashAlgo; /**< The hash algo selected during ZRTP negotiation */ uint8_t keyAgreementAlgo; /**< The key agreement algo selected during ZRTP negotiation */ uint8_t sasAlgo; /**< The SAS rendering algo selected during ZRTP negotiation */ uint8_t cacheMismatch; /**< Flag set to 1 in case of ZRTP cache mismatch, may occurs only on first channel(the one computing SAS) */ uint8_t auxSecretMismatch; /**< Flag set to BZRTP_AUXSECRET_MATCH, BZRTP_AUXSECRET_MISMATCH or BZRTP_AUXSECRET_UNSET, may occurs only on first channel(the one computing SAS), in case of mismatch it may be ignored and we can still validate the SAS */ } bzrtpSrtpSecrets_t; /* define auxSecretMismatch flag codes */ #define BZRTP_AUXSECRET_MATCH 0x00 #define BZRTP_AUXSECRET_MISMATCH 0x01 #define BZRTP_AUXSECRET_UNSET 0x02 /* define message levels */ #define BZRTP_MESSAGE_ERROR 0x00 #define BZRTP_MESSAGE_WARNING 0x01 #define BZRTP_MESSAGE_LOG 0x02 #define BZRTP_MESSAGE_DEBUG 0x03 /* define message codes */ #define BZRTP_MESSAGE_CACHEMISMATCH 0x01 #define BZRTP_MESSAGE_PEERVERSIONOBSOLETE 0x02 #define BZRTP_MESSAGE_PEERNOTBZRTP 0x03 /** * Function pointer used by bzrtp to free memory allocated by callbacks. **/ typedef void (*zrtpFreeBuffer_callback)(void *); /** * @brief All the callback functions provided by the client needed by the ZRTP engine */ typedef struct bzrtpCallbacks_struct { /* messaging status and warnings */ int (* bzrtp_statusMessage)(void *clientData, const uint8_t messageLevel, const uint8_t messageId, const char *messageString); /**< Sending messages to caller: error, warnings, logs, the messageString can be NULL or a NULL terminated string */ int bzrtp_messageLevel; /**< Filter calls to this callback to levels inferiors to this setting (BZRTP_MESSAGE_ERROR, BZRTP_MESSAGE_WARNING, BZRTP_MESSAGE_LOG, BZRTP_MESSAGE_DEBUG )*/ /* sending packets */ int (* bzrtp_sendData)(void *clientData, const uint8_t *packetString, uint16_t packetLength); /**< Send a ZRTP packet to peer. Shall return 0 on success */ /* dealing with SRTP session */ int (* bzrtp_srtpSecretsAvailable)(void *clientData, const bzrtpSrtpSecrets_t *srtpSecrets, uint8_t part); /**< Send the srtp secrets to the client, for either sender, receiver or both according to the part parameter value. Client may wait for the end of ZRTP process before using it */ int (* bzrtp_startSrtpSession)(void *clientData, const bzrtpSrtpSecrets_t *srtpSecrets, int32_t verified); /**< ZRTP process ended well, client is given the SAS and informations about the crypto algo used during ZRTP negotiation. He may start his SRTP session if not done when calling srtpSecretsAvailable */ /* ready for exported keys */ int (* bzrtp_contextReadyForExportedKeys)(void *clientData, int zuid, uint8_t role); /**< Tell the client that this is the time to create any exported keys, s0 is erased just after the call to this callback. Callback is given the peerZID and zuid to adress the correct node in cache and current role which is needed to set a pair of keys for IM encryption */ } bzrtpCallbacks_t; #define ZRTP_MAGIC_COOKIE 0x5a525450 #define ZRTP_VERSION "1.10" /* error code definition */ #define BZRTP_ERROR_INVALIDCALLBACKID 0x0001 #define BZRTP_ERROR_CONTEXTNOTREADY 0x0002 #define BZRTP_ERROR_INVALIDCONTEXT 0x0004 #define BZRTP_ERROR_MULTICHANNELNOTSUPPORTEDBYPEER 0x0008 #define BZRTP_ERROR_UNABLETOADDCHANNEL 0x0010 #define BZRTP_ERROR_UNABLETOSTARTCHANNEL 0x0020 #define BZRTP_ERROR_OUTPUTBUFFER_LENGTH 0x0040 #define BZRTP_ERROR_HELLOHASH_MISMATCH 0x0080 #define BZRTP_ERROR_CHANNELALREADYSTARTED 0x0100 #define BZRTP_ERROR_CACHEDISABLED 0x0200 #define BZRTP_ERROR_CACHEMIGRATIONFAILED 0x0400 #define BZRTP_ERROR_CACHE_PEERNOTFOUND 0x0800 /* channel status definition */ #define BZRTP_CHANNEL_NOTFOUND 0x1000 #define BZRTP_CHANNEL_INITIALISED 0x1001 #define BZRTP_CHANNEL_ONGOING 0x1002 #define BZRTP_CHANNEL_SECURE 0x1004 #define BZRTP_CHANNEL_ERROR 0x1008 /* role mapping */ #define BZRTP_ROLE_INITIATOR 0 #define BZRTP_ROLE_RESPONDER 1 /* cache related value */ #define BZRTP_CACHE_SETUP 0x2000 #define BZRTP_CACHE_UPDATE 0x2001 #define BZRTP_CACHE_DATA_NOTFOUND 0x2002 #define BZRTP_CACHE_PEER_STATUS_UNKNOWN 0x2010 #define BZRTP_CACHE_PEER_STATUS_VALID 0x2011 #define BZRTP_CACHE_PEER_STATUS_INVALID 0x2012 /* cache function error codes */ #define BZRTP_ZIDCACHE_INVALID_CONTEXT 0x2101 #define BZRTP_ZIDCACHE_INVALID_CACHE 0x2102 #define BZRTP_ZIDCACHE_UNABLETOUPDATE 0x2103 #define BZRTP_ZIDCACHE_UNABLETOREAD 0x2104 #define BZRTP_ZIDCACHE_BADINPUTDATA 0x2105 #define BZRTP_ZIDCACHE_RUNTIME_CACHELESS 0x2110 /** * @brief bzrtpContext_t The ZRTP engine context * Store current state, timers, HMAC and encryption keys */ typedef struct bzrtpContext_struct bzrtpContext_t; #ifdef __cplusplus extern "C" { #endif /** * Create context structure and initialise it * * @return The ZRTP engine context data * */ BZRTP_EXPORT bzrtpContext_t *bzrtp_createBzrtpContext(void); /** * @brief Perform initialisation which can't be done without ZIDcache acces * - get ZID and create the first channel context * * @param[in] context The context to initialise * @param[in] selfSSRC The SSRC given to the first channel context created within the zrtpContext * * @return 0 on success */ BZRTP_EXPORT int bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC); /** * Free memory of context structure to a channel, if all channels are freed, free the global zrtp context * @param[in] context Context hosting the channel to be destroyed.(note: the context zrtp context itself is destroyed with the last channel) * @param[in] selfSSRC The SSRC identifying the channel to be destroyed * * @return the number of channel still active in this ZRTP context */ BZRTP_EXPORT int bzrtp_destroyBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC); /** * @brief Allocate a function pointer to the callback function identified by his id * @param[in/out] context The zrtp context to set the callback function * @param[in] cbs A structure containing all the callbacks to supply. * * @return 0 on success * */ BZRTP_EXPORT int bzrtp_setCallbacks(bzrtpContext_t *context, const bzrtpCallbacks_t *cbs); /** * @brief Set the pointer allowing cache access, * this version of the function get a mutex to lock the cache when accessing it * * @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled * @param[in] selfURI Local URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] peerURI Peer URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access * * @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise */ BZRTP_EXPORT int bzrtp_setZIDCache_lock(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI, bctbx_mutex_t *zidCacheMutex); /** * @brief Set the pointer allowing cache access * * @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled * @param[in] selfURI Local URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] peerURI Peer URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * * @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise */ BZRTP_EXPORT int bzrtp_setZIDCache(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI); /** * @brief Set the client data pointer in a channel context * This pointer is returned to the client by the callbacks function, used to store associated contexts (RTP session) * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel to be linked to the client Data * @param[in] clientData The clientData pointer, casted to a (void *) * * @return 0 on success * */ BZRTP_EXPORT int bzrtp_setClientData(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, void *clientData); /** * @brief Add a channel to an existing context * * @param[in/out] zrtpContext The zrtp context who will get the additionnal channel * @param[in] selfSSRC The SSRC given to the channel context * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_addChannel(bzrtpContext_t *zrtpContext, uint32_t selfSSRC); /** * @brief Start the state machine of the specified channel * To be able to start an addional channel, we must be in secure state * * @param[in/out] zrtpContext The ZRTP context hosting the channel to be started * @param[in] selfSSRC The SSRC identifying the channel to be started(will start sending Hello packets and listening for some) * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_startChannelEngine(bzrtpContext_t *zrtpContext, uint32_t selfSSRC); /** * @brief Send the current time to a specified channel, it will check if it has to trig some timer * * @param[in/out] zrtpContext The ZRTP context hosting the channel * @param[in] selfSSRC The SSRC identifying the channel * @param[in] timeReference The current time in ms * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_iterate(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint64_t timeReference); /** * @brief Process a received message * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel receiving the message * @param[in] zrtpPacketString The packet received * @param[in] zrtpPacketStringLength Length of the packet in bytes * * @return 0 on success, errorcode otherwise */ BZRTP_EXPORT int bzrtp_processMessage(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *zrtpPacketString, uint16_t zrtpPacketStringLength); /** * @brief Called by user when the SAS has been verified * * @param[in/out] zrtpContext The ZRTP context we're dealing with */ BZRTP_EXPORT void bzrtp_SASVerified(bzrtpContext_t *zrtpContext); /** * @brief Called by user when the SAS has been set to unverified * * @param[in/out] zrtpContext The ZRTP context we're dealing with */ BZRTP_EXPORT void bzrtp_resetSASVerified(bzrtpContext_t *zrtpContext); /** * @brief Reset the retransmission timer of a given channel. * Packets will be sent again if appropriate: * - when in responder role, zrtp engine only answer to packets sent by the initiator. * - if we are still in discovery phase, Hello or Commit packets will be resent. * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel to reset * * return 0 on success, error code otherwise */ BZRTP_EXPORT int bzrtp_resetRetransmissionTimer(bzrtpContext_t *zrtpContext, uint32_t selfSSRC); /** * @brief Get the supported crypto types * * @param[int] zrtpContext The ZRTP context we're dealing with * @param[in] algoType mapped to defines, must be in [ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE] * @param[out] supportedTypes mapped to uint8_t value of the 4 char strings giving the supported types as string according to rfc section 5.1.2 to 5.1.6 * * @return number of supported types, 0 on error */ BZRTP_EXPORT uint8_t bzrtp_getSupportedCryptoTypes(bzrtpContext_t *zrtpContext, uint8_t algoType, uint8_t supportedTypes[7]); /** * @brief set the supported crypto types * * @param[int/out] zrtpContext The ZRTP context we're dealing with * @param[in] algoType mapped to defines, must be in [ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE] * @param[in] supportedTypes mapped to uint8_t value of the 4 char strings giving the supported types as string according to rfc section 5.1.2 to 5.1.6 * @param[in] supportedTypesCount number of supported crypto types */ BZRTP_EXPORT void bzrtp_setSupportedCryptoTypes(bzrtpContext_t *zrtpContext, uint8_t algoType, uint8_t supportedTypes[7], uint8_t supportedTypesCount); /** * @brief Set the peer hello hash given by signaling to a ZRTP channel * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * @param[in] peerHelloHashHexString A NULL terminated string containing the hexadecimal form of the hash received in signaling, * may contain ZRTP version as header. * @param[in] peerHelloHashHexStringLength Length of hash string (shall be at least 64 as the hash is a SHA256 so 32 bytes, * more if it contains the version header) * * @return 0 on success, errorcode otherwise */ BZRTP_EXPORT int bzrtp_setPeerHelloHash(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *peerHelloHashHexString, size_t peerHelloHashHexStringLength); /** * @brief Get the self hello hash from ZRTP channel * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * @param[out] output A NULL terminated string containing the hexadecimal form of the hash received in signaling, * contain ZRTP version as header. Buffer must be allocated by caller. * @param[in] outputLength Length of output buffer, shall be at least 70 : 5 chars for version, 64 for the hash itself, SHA256), NULL termination * * @return 0 on success, errorcode otherwise */ BZRTP_EXPORT int bzrtp_getSelfHelloHash(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *output, size_t outputLength); /** * @brief Get the channel status * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * * @return BZRTP_CHANNEL_NOTFOUND no channel matching this SSRC doesn't exists in the zrtp context * BZRTP_CHANNEL_INITIALISED channel initialised but not started * BZRTP_CHANNEL_ONGOING ZRTP key exchange in ongoing * BZRTP_CHANNEL_SECURE Channel is secure * BZRTP_CHANNEL_ERROR An error occured on this channel */ BZRTP_EXPORT int bzrtp_getChannelStatus(bzrtpContext_t *zrtpContext, uint32_t selfSSRC); /** * @brief Set Auxiliary Secret for this channel(shall be used only on primary audio channel) * The given auxSecret is appended to any aux secret found in ZIDcache * This function must be called before reception of peerHello packet * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] auxSecret A buffer holding the auxiliary shared secret to use (see RFC 6189 section 4.3) * @param[in] auxSecretLength lenght of the previous buffer * * @return 0 on success, error code otherwise * * @note The auxiliary shared secret mechanic is used by LIMEv2 for encryption security purposes but might be used for its original purpose in a regular * ZRTP session if it becomes necessary in the future, or by another encryption engine for example. In that case the API will need an adaptation work. */ BZRTP_EXPORT int bzrtp_setAuxiliarySharedSecret(bzrtpContext_t *zrtpContext, const uint8_t *auxSecret, size_t auxSecretLength); /** * @brief Get the ZRTP auxiliary shared secret mismatch status * @param[in] ctx MSZRTP context * @return 0 on match, 1 otherwise */ BZRTP_EXPORT uint8_t bzrtp_getAuxiliarySharedSecretMismatch(bzrtpContext_t *zrtpContext); /*** Cache related functions ***/ /** * @brief Check the given sqlite3 DB and create requested tables if needed * Also manage DB schema upgrade * @param[in/out] db Pointer to the sqlite3 db open connection * Use a void * to keep this API when building cacheless * * @return 0 on success, BZRTP_CACHE_SETUP if cache was empty, BZRTP_CACHE_UPDATE if db structure was updated, error code otherwise */ BZRTP_EXPORT BZRTP_DEPRECATED int bzrtp_initCache(void *db); /** * @brief Check the given sqlite3 DB and create requested tables if needed * Also manage DB schema upgrade * * this version of the function gets a mutex to lock the cache when accessing it * * @param[in/out] db Pointer to the sqlite3 db open connection * Use a void * to keep this API when building cacheless * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on success, BZRTP_CACHE_SETUP if cache was empty, BZRTP_CACHE_UPDATE if db structure was updated, error code otherwise */ BZRTP_EXPORT int bzrtp_initCache_lock(void *db, bctbx_mutex_t *zidCacheMutex); /** * @brief : retrieve ZID from cache * ZID is randomly generated if cache is empty or inexistant * ZID is randomly generated in case of cacheless implementation(db argument is NULL) * * @param[in/out] db sqlite3 database(or NULL if we don't use cache at runtime) * Use a void * to keep this API when building cacheless * @param[in] selfURI the sip uri of local user, NULL terminated string * @param[out] selfZID the ZID, retrieved from cache or randomly generated * @param[in] RNGContext A RNG context used to generate ZID if needed * * @return 0 on success, or BZRTP_CACHE_DATA_NOTFOUND if no ZID matching the URI was found and no RNGContext is given to generate one */ BZRTP_EXPORT BZRTP_DEPRECATED int bzrtp_getSelfZID(void *db, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext); /** * @brief : retrieve ZID from cache * ZID is randomly generated if cache is empty or inexistant * ZID is randomly generated in case of cacheless implementation(db argument is NULL) * this version of the function gets a mutex to lock the cache when accessing it * * @param[in/out] db sqlite3 database(or NULL if we don't use cache at runtime) * Use a void * to keep this API when building cacheless * @param[in] selfURI the sip uri of local user, NULL terminated string * @param[out] selfZID the ZID, retrieved from cache or randomly generated * @param[in] RNGContext A RNG context used to generate ZID if needed * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on success, or BZRTP_CACHE_DATA_NOTFOUND if no ZID matching the URI was found and no RNGContext is given to generate one */ BZRTP_EXPORT int bzrtp_getSelfZID_lock(void *db, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext, bctbx_mutex_t *zidCacheMutex); /** * @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be inserted, lengths of theses values * All three arrays must be the same lenght: columnsCount * If the row isn't present in the given table, it will be inserted * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to write in the db, must already exists. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to update * @param[in] values An array of buffers containing the values to insert/update matching the order of columns array * @param[in] lengths An array of integer containing the lengths of values array buffer matching the order of columns array * @param[in] columnsCount length common to columns,values and lengths arrays * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT BZRTP_DEPRECATED int bzrtp_cache_write(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount); /** * @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be inserted, lengths of theses values * All three arrays must be the same lenght: columnsCount * If the row isn't present in the given table, it will be inserted * this version of the function gets a mutex to lock the cache when accessing it * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to write in the db, must already exists. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to update * @param[in] values An array of buffers containing the values to insert/update matching the order of columns array * @param[in] lengths An array of integer containing the lengths of values array buffer matching the order of columns array * @param[in] columnsCount length common to columns,values and lengths arrays * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_cache_write_lock(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount, bctbx_mutex_t *zidCacheMutex); /** * @brief Read data from specified table/columns from cache adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be read, and the number of colums to be read * Produce an array of values(uint8_t arrays) and a array of corresponding lengths * Values memory is allocated by this function and must be freed by caller * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to read in the db. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to read, the array's length is columnsCount * @param[out] values An array of uint8_t pointers, each one will be allocated to the read value and they must be freed by caller * @param[out] lengths An array of integer containing the lengths of values array buffer read * @param[in] columnsCount length common to columns,values and lengths arrays * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT BZRTP_DEPRECATED int bzrtp_cache_read(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount); /** * @brief Read data from specified table/columns from cache adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be read, and the number of colums to be read * Produce an array of values(uint8_t arrays) and a array of corresponding lengths * Values memory is allocated by this function and must be freed by caller * this version of the function gets a mutex to lock the cache when accessing it * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to read in the db. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to read, the array's length is columnsCount * @param[out] values An array of uint8_t pointers, each one will be allocated to the read value and they must be freed by caller * @param[out] lengths An array of integer containing the lengths of values array buffer read * @param[in] columnsCount length common to columns,values and lengths arrays * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_cache_read_lock(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount, bctbx_mutex_t *zidCacheMutex); /** * @brief Perform migration from xml version to sqlite3 version of cache * Warning: new version of cache associate a ZID to each local URI, the old one did not * the migration function will associate any data in the cache to the sip URI given in parameter which shall be the default URI * @param[in] cacheXml a pointer to an xmlDocPtr structure containing the old cache to be migrated * @param[in/out] cacheSqlite a pointer to an sqlite3 structure containing a cache initialised using bzrtp_cache_init function * @param[in] selfURI default sip URI for this end point, NULL terminated char * * @return 0 on success, BZRTP_ERROR_CACHEDISABLED when bzrtp was not compiled with cache enabled, BZRTP_ERROR_CACHEMIGRATIONFAILED on error during migration */ BZRTP_EXPORT int bzrtp_cache_migration(void *cacheXmlPtr, void *cacheSqlite, const char *selfURI); /* * @brief Allow client to compute an exported according to RFC section 4.5.2 * Check the context is ready(we already have a master exported key and KDF context) * and run KDF(master exported key, "Label", KDF_Context, negotiated hash Length) * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] label Label used in the KDF * @param[in] labelLength Length of previous buffer * @param[out] derivedKey Buffer to store the derived key * @param[in/out] derivedKeyLength Length of previous buffer(updated to fit actual length of data produced if too long) * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_exportKey(bzrtpContext_t *zrtpContext, char *label, size_t labelLength, uint8_t *derivedKey, size_t *derivedKeyLength); /** * @brief Retrieve from bzrtp cache the trust status(based on the previously verified flag) of a peer URI * * This function will return the SAS validation status of the active device * associated to the given peerURI. * * Important note about the active device: * - any ZRTP exchange with a peer device will set it to be the active one for its sip:uri * - the concept of active device is shared between local accounts if there are several of them, it means that : * - if you have several local users on your device, each of them may have an entry in the ZRTP cache with a particular peer sip:uri (if they ever got in contact with it) but only one of this entry is set to active * - this function will return the status associated to the last updated entry without any consideration for the local users it is associated with * - any call where the SAS was neither accepted or rejected will not update the trust status but will set as active device for the peer sip:uri the one involved in the call * * This function is intended for use in a mono-device environment. * * @param[in] dbPointer Pointer to an already opened sqlite db * @param[in] peerURI The peer sip:uri we're interested in * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return one of: * - BZRTP_CACHE_PEER_STATUS_UNKNOWN : this uri is not present in cache OR during calls with the active device, SAS never was validated or rejected * Note: once the SAS has been validated or rejected, the status will never return to UNKNOWN(unless you delete your cache) * - BZRTP_CACHE_PEER_STATUS_VALID : the active device status is set to valid * - BZRTP_CACHE_PEER_STATUS_INVALID : the active peer device status is set to invalid * */ BZRTP_EXPORT int bzrtp_cache_getPeerStatus_lock(void *dbPointer, const char *peerURI, bctbx_mutex_t *zidCacheMutex); #ifdef __cplusplus } #endif #endif /* ifndef BZRTP_H */ bzrtp-4.4.13/include/cryptoUtils.h000066400000000000000000000233541364144501400171120ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CRYPTOUTILS_H #define CRYPTOUTILS_H #include "typedef.h" #include "packetParser.h" /** Return available crypto functions. For now we have * * - Hash: HMAC-SHA256(Mandatory) * - CipherBlock: AES128(Mandatory) * - Auth Tag: HMAC-SHA132 and HMAC-SHA180 (These are mandatory for SRTP and depends on the SRTP implementation thus we can just suppose they are both available) * - Key Agreement: DHM3k(Mandatory), DHM2k(optional and shall not be used except on low power devices) * - Sas: base32(Mandatory), b256(pgp words) */ uint8_t bzrtpUtils_getAvailableCryptoTypes(uint8_t algoType, uint8_t availableTypes[7]); /** * * @brief ZRTP Key Derivation Function as in rfc 4.5.1 * * KDF(KI, Label, Context, L) = HMAC(KI, i || Label || * 0x00 || Context || L) * where * - i is a 32-bits integer fixed to 0x00000001 * - L is a 32-bit big-endian positive * integer, not to exceed the length in bits of the output of the HMAC. * The output of the KDF is truncated to the leftmost L bits. * * @param[in] key The key for HMAC * @param[in] keyLength Length of the key in bytes * @param[in] label A string to be included in the hash * @param[in] labelLength Length of the label in bytes * @param[in] context a context string for the key derivation * @param[in] contextLength Length of the context string in bytes * @param[in] hmacLength The output of the KDF is the HMAC truncated to the leftmost L bytes * @param[in] hmacFunction The hashmac function to be used to compute the KDF * @param[out] output A buffer to store the hmacLength bytes of output * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_keyDerivationFunction(const uint8_t *key, const size_t keyLength, const uint8_t *label, const size_t labelLength, const uint8_t *context, const size_t contextLength, const uint16_t hmacLength, void (*hmacFunction)(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t, uint8_t *), uint8_t *output); /** * @brief SAS rendering from 32 bits to 4 characters * Function defined in rfc section 5.1.6 * * @param[in] sas The 32 bits SAS * @param[out] output The 4 chars string to be displayed to user for vocal confirmation * */ void bzrtp_base32(uint32_t sas, char *output, int outputSize); /** * @brief SAS rendering from 32 bits to pgp word list * Function defined in rfc section 5.1.6 * * @param[in] sas The 32 bits SAS * @param[out] output The output list. Passed in array must be at least 32 bytes * */ void bzrtp_base256(uint32_t sas, char *output, int outputSize); /** * @brief CRC32 as defined in RFC4960 Appendix B - Polynomial is 0x1EDC6F41 * * CRC is computed in reverse bit mode (least significant bit first within each byte) * reversed value of polynom (0x82F63B78) was used to compute the lookup table (source * http://en.wikipedia.org/wiki/Cyclic_redundancy_check#Commonly_used_and_standardized_CRCs) * * @param[in] input input data * @param[in] length length of data in bytes * * @return the 32 bits CRC value * */ BZRTP_EXPORT uint32_t bzrtp_CRC32(uint8_t *input, uint16_t length); /* error code for the cryptoAlgoAgreement and function pointer update functions */ #define ZRTP_CRYPTOAGREEMENT_INVALIDCONTEXT 0x1001 #define ZRTP_CRYPTOAGREEMENT_INVALIDMESSAGE 0x1002 #define ZRTP_CRYPTOAGREEMENT_INVALIDSELFALGO 0x1003 #define ZRTP_CRYPTOAGREEMENT_NOCOMMONALGOFOUND 0x1004 #define ZRTP_CRYPTOAGREEMENT_INVALIDCIPHER 0x1005 #define ZRTP_CRYPTOAGREEMENT_INVALIDHASH 0x1006 #define ZRTP_CRYPTOAGREEMENT_INVALIDAUTHTAG 0x1007 #define ZRTP_CRYPTOAGREEMENT_INVALIDSAS 0x1008 /** * @brief select a key agreement algorithm from the one available in context and the one provided by * peer in Hello Message as described in rfc section 4.1.2 * - other algorithm are selected according to availability and selected key agreement as described in * rfc section 5.1.5 * The other algorithm choice will finally be set by the endpoint acting as initiator in the commit packet * * @param[in] zrtpContext The context contains the list of available algo * @param[out] zrtpChannelContext The bzrtp channel context to be updated * @param[in] peerHelloMessage The peer hello message containing his set of available algos * * return 0 on succes, error code otherwise * */ BZRTP_EXPORT int bzrtp_cryptoAlgoAgreement(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpHelloMessage_t *peerHelloMessage); /** * @brief Update context crypto function pointer according to related values of choosen algorithms fields (hashAlgo, cipherAlgo, etc..) * * @param[in/out] zrtpChannelContext The bzrtp channel context to be updated * * @return 0 on succes */ BZRTP_EXPORT int bzrtp_updateCryptoFunctionPointers(bzrtpChannelContext_t *zrtpChannelContext); /** * @brief Select common algorithm from the given array where algo are represented by their 4 chars string defined in rfc section 5.1.2 to 5.1.6 * Master array is the one given the preference order * All algo are designed by their uint8_t mapped values * * @param[in] masterArray The ordered available algo, result will follow this ordering * @param[in] masterArrayLength Number of valids element in the master array * @param[in] slaveArray The available algo, order is not taken in account * @param[in] slaveArrayLength Number of valids element in the slave array * @param[out] commonArray Common algorithms found, max size 7 * * @return the number of common algorithms found */ uint8_t selectCommonAlgo(uint8_t masterArray[7], uint8_t masterArrayLength, uint8_t slaveArray[7], uint8_t slaveArrayLength, uint8_t commonArray[7]); /** * @brief add mandatory crypto functions if they are not already included * - Hash function * - Cipher Block * - Auth Tag * - Key agreement * - SAS * * @param[in] algoType mapped to defines, must be in [ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE] * @param[in/out] algoTypes mapped to uint8_t value of the 4 char strings giving the algo types as string according to rfc section 5.1.2 to 5.1.6 * @param[in/out] algoTypesCount number of algo types */ BZRTP_EXPORT void bzrtp_addMandatoryCryptoTypesIfNeeded(uint8_t algoType, uint8_t algoTypes[7], uint8_t *algoTypesCount); /** * @brief Map the string description of algo type to an int defined in cryptoWrapper.h * * @param[in] algoType A 4 chars string containing the algo type as listed in rfc sections 5.1.2 to 5.1.6 * @param[in] algoFamily The integer mapped algo family (ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, * ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE) * @return The int value mapped to the algo type, ZRTP_UNSET_ALGO on error */ BZRTP_EXPORT uint8_t bzrtp_cryptoAlgoTypeStringToInt(uint8_t algoType[4], uint8_t algoFamily); /** * @brief Unmap the string description of algo type to an int defined in cryptoWrapper.h * * @param[in] algoTypeInt The integer algo type defined in crypoWrapper.h * @param[in] algoFamily The string code for the algorithm as defined in rfc 5.1.2 to 5.1.6 */ BZRTP_EXPORT void bzrtp_cryptoAlgoTypeIntToString(uint8_t algoTypeInt, uint8_t algoTypeString[4]); /** * @brief Destroy a key by setting it to a random number * Key is not freed, caller must deal with memory management. * Does nothing if the key pointer is NULL * * @param[in/out] key The key to be destroyed * @param[in] keyLength The keyLength in bytes * @param[in] rngContext The context for RNG */ BZRTP_EXPORT void bzrtp_DestroyKey(uint8_t *key, uint8_t keyLength, void *rngContext); /** * @brief Convert an hexadecimal string into the corresponding byte buffer * * @param[out] outputBytes The output bytes buffer, must have a length of half the input string buffer * @param[in] inputString The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0) * @param[in] inputStringLength The length in chars of the string buffer, output is half this length */ void bzrtp_strToUint8(uint8_t *outputBytes, uint8_t *inputString, uint16_t inputStringLength); /** * @brief Convert a byte buffer into the corresponding hexadecimal string * * @param[out] outputString The output string buffer, must have a length of twice the input bytes buffer * @param[in] inputBytes The input bytes buffer * @param[in] inputBytesLength The length in bytes buffer, output is twice this length */ void bzrtp_int8ToStr(uint8_t *outputString, uint8_t *inputBytes, uint16_t inputBytesLength); /** * @brief convert an hexa char [0-9a-fA-F] into the corresponding unsigned integer value * Any invalid char will be converted to zero without any warning * * @param[in] inputChar a char which shall be in range [0-9a-fA-F] * * @return the unsigned integer value in range [0-15] */ uint8_t bzrtp_charToByte(uint8_t inputChar); /** * @brief convert a byte which value is in range [0-15] into an hexa char [0-9a-fA-F] * * @param[in] inputByte an integer which shall be in range [0-15] * * @return the hexa char [0-9a-f] corresponding to the input */ uint8_t bzrtp_byteToChar(uint8_t inputByte); #endif /* CRYPTOUTILS_H */ bzrtp-4.4.13/include/packetParser.h000066400000000000000000000500171364144501400171710ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PACKETPARSER_H #define PACKETPARSER_H #include #include "bzrtp/bzrtp.h" /* header of ZRTP packet is 12 bytes : Preambule/Sequence Number + ZRTP Magic Cookie + SSRC */ #define ZRTP_PACKET_HEADER_LENGTH 12 #define ZRTP_PACKET_CRC_LENGTH 4 #define ZRTP_PACKET_OVERHEAD 16 #define BZRTP_PARSER_ERROR_INVALIDCRC 0xa001 #define BZRTP_PARSER_ERROR_INVALIDPACKET 0xa002 #define BZRTP_PARSER_ERROR_OUTOFORDER 0xa004 #define BZRTP_PARSER_ERROR_INVALIDMESSAGE 0xa008 #define BZRTP_PARSER_ERROR_INVALIDCONTEXT 0xa010 #define BZRTP_PARSER_ERROR_UNMATCHINGCONFIRMMAC 0xa020 #define BZRTP_PARSER_ERROR_UNMATCHINGSSRC 0xa040 #define BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN 0xa080 #define BZRTP_PARSER_ERROR_UNMATCHINGMAC 0xa100 #define BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE 0xa200 #define BZRTP_PARSER_ERROR_UNMATCHINGHVI 0xa400 #define BZRTP_BUILDER_ERROR_INVALIDPACKET 0x5001 #define BZRTP_BUILDER_ERROR_INVALIDMESSAGE 0x5002 #define BZRTP_BUILDER_ERROR_INVALIDMESSAGETYPE 0x5004 #define BZRTP_BUILDER_ERROR_UNKNOWN 0x5008 #define BZRTP_BUILDER_ERROR_INVALIDCONTEXT 0x5010 #define BZRTP_CREATE_ERROR_INVALIDMESSAGETYPE 0x0a01 #define BZRTP_CREATE_ERROR_UNABLETOCREATECRYPTOCONTEXT 0x0a02 #define BZRTP_CREATE_ERROR_INVALIDCONTEXT 0x0a04 /* map all message type to an uint8_t value */ #define MSGTYPE_INVALID 0x00 #define MSGTYPE_HELLO 0x01 #define MSGTYPE_HELLOACK 0x02 #define MSGTYPE_COMMIT 0x03 #define MSGTYPE_DHPART1 0x04 #define MSGTYPE_DHPART2 0x05 #define MSGTYPE_CONFIRM1 0x06 #define MSGTYPE_CONFIRM2 0x07 #define MSGTYPE_CONF2ACK 0x08 #define MSGTYPE_ERROR 0x10 #define MSGTYPE_ERRORACK 0x11 #define MSGTYPE_GOCLEAR 0x12 #define MSGTYPE_CLEARACK 0x13 #define MSGTYPE_SASRELAY 0x14 #define MSGTYPE_RELAYACK 0x15 #define MSGTYPE_PING 0x16 #define MSGTYPE_PINGACK 0x17 /** * @brief Store all zrtpPacket informations * according to type a specific structure type is mapped to the void * data pointer */ typedef struct bzrtpPacket_struct { uint16_t sequenceNumber; /**< set by packet parser to enable caller to retrieve the packet sequence number. This field is not used buy the packet creator, sequence number is given as a parameter when converting the message to a packet string. Used only when parsing a string into a packet struct */ uint32_t sourceIdentifier; /**< the SSRC of current RTP stream */ uint8_t messageType; /**< the ZRTP message type mapped from strings to hard defined byte */ uint16_t messageLength; /**< the ZRTP message length in bytes - the message length indicated in the message itself is in 32 bits words. Is not the packet length(do not include packet header and CRC) */ void *messageData; /**< a pointer to the structure containing all the message field according to message type */ uint8_t *packetString; /**< used to stored the string version of the packet build from the message data or keep a string copy of received packets */ } bzrtpPacket_t; /** * Structure definition for all zrtp message type according to rfc section 5.2 to 5.16 * */ /** * @brief Hello Message rfc 5.2 */ typedef struct bzrtpHelloMessage_struct { uint8_t version[4]; /**< a string defining the current version, shall be 1.10 */ uint8_t clientIdentifier[17]; /**< a string identifing the vendor and release of ZRTP software, actual content is 16, but last character forced to '\0' */ uint8_t H3[32]; /**< the hash image H3 (256 bits) */ uint8_t ZID[12]; /**< unique identifier for ZRTP endpoint (96 bits) */ uint8_t S; /**< The signature-capable flag. If signatures are not supported, the (S) flag MUST be set to zero (1 bit) */ uint8_t M; /**< The MiTM flag (M) is a Boolean that is set to true if and only if this Hello message is sent from a device, usually a PBX, that has the capability to send an SASrelay message (1 bit) **/ uint8_t P; /**< The Passive flag (P) is a Boolean normally set to false, and is set to true if and only if this Hello message is sent from a device that is configured to never send a Commit message (Section 5.4). This would mean it cannot initiate secure sessions, but may act as a responder. (1 bit) */ uint8_t hc; /**< hash count -zrtpPacket set to 0 means we support only HMAC-SHA256 (4 bits) */ uint8_t supportedHash[7]; /**< list of supported hash algorithms mapped to uint8_t */ uint8_t cc; /**< cipher count - set to 0 means we support only AES128-CFB128 (4 bits) */ uint8_t supportedCipher[7]; /**< list of supported cipher algorithms mapped to uint8_t */ uint8_t ac; /**< auth tag count - set to 0 mean we support only HMAC-SHA1-32 (4 bits) */ uint8_t supportedAuthTag[7]; /**< list of supported SRTP authentication tag algorithms mapped to uint8_t */ uint8_t kc; /**< key agreement count - set to 0 means we support only Diffie-Hellman-Merkle 3072 (4 bits) */ uint8_t supportedKeyAgreement[7]; /**< list of supported key agreement algorithms mapped to uint8_t */ uint8_t sc; /**< sas count - set to 0 means we support only base32 (4 bits) */ uint8_t supportedSas[7]; /**< list of supported Sas representations (4 chars string) */ uint8_t MAC[8]; /**< HMAC over the whole message, keyed by the hash image H2 (64 bits)*/ } bzrtpHelloMessage_t; /** * @brief Hello ACK Message rfc 5.3 * This message contains no data but only a length and message type which are stored in the bzrtpPacket_t structure * There the no need to define a structure type for this packet */ /** * * @brief Commit Message rfc 5.4 * This message can be of 3 different types: DHM, PreShared and Multistream, some field of it may be used only by certain type of message * It is generated by the initiator (see section 4.2 for commit contention) */ typedef struct bzrtpCommitMessage_struct { uint8_t H2[32]; /**< the hash image H2 (256 bits) */ uint8_t ZID[12]; /**< initiator's unique identifier for ZRTP endpoint (96 bits) */ uint8_t hashAlgo; /**< the hash algorithm identifier rfc section 5.1.2 mapped to an integer */ uint8_t cipherAlgo; /**< the cipher algorithm identifier rfc section 5.1.3 mapped to an integer */ uint8_t authTagAlgo; /**< the auth tag algorithm identifier rfc section 5.1.4 mapped to an integer */ uint8_t keyAgreementAlgo; /**< the key agreement algorithm identifier rfc section 5.1.5. It can either be a key exchange algorithm or the commit packet type in case of preShared or multistream commit message mapped to an integer */ uint8_t sasAlgo; /**< the sas rendering algorithm identifier rfc section 5.1.6 mapped to an integer */ uint8_t hvi[32]; /**< only for DH commit : a hash of initiator's DHPart2 and responder's Hello message rfc section 4.4.1.1 */ uint8_t nonce[16]; /**< only for preShared or Multistream modes : a 128 bits random number generated by the initiator */ uint8_t keyID[8]; /**< only for preShared mode : the preshared key identifier */ uint8_t MAC[8]; /**< HMAC over the whole message, keyed by the hash image H1 (64 bits)*/ } bzrtpCommitMessage_t; /** * * @brief DHPart Message rfc 5.5 and rfc 5.6 * DHPart1 and DHPart2 message have the same structure * DHPart1 is generated by the responder, and DHPart2 by the initiator */ typedef struct bzrtpDHPartMessage_struct { uint8_t H1[32]; /**< the hash image H1 (256 bits) */ uint8_t rs1ID[8]; /**< hash of the retained secret 1 (64 bits) */ uint8_t rs2ID[8]; /**< hash of the retained secret 2 (64 bits) */ uint8_t auxsecretID[8]; /**< hash of the auxiliary shared secret (64 bits) */ uint8_t pbxsecretID[8]; /**< hash of the trusted MiTM PBX shared secret pbxsecret, defined in section 7.3.1 (64 bits) */ uint8_t *pv; /* Key exchange public value (length depends on key agreement type) */ uint8_t MAC[8]; /**< HMAC over the whole message, keyed by the hash image H1 (64 bits)*/ } bzrtpDHPartMessage_t; /** * * @brief Confirm Message rfc 5.7 * Confirm1 and Confirm2 messages have the same structure * Confirm1 is generated by the responder and Confirm2 by the initiator * Part of the message is encrypted using the negotiated block cipher for media encryption. Keys ares zrtpkeyr for responder and zrtpkeyi for initiator */ typedef struct bzrtpConfirmMessage_struct { uint8_t confirm_mac[8]; /**< a MAC computed over the encrypted part of the message (64 bits) */ uint8_t CFBIV[16]; /**< The CFB Initialization Vector is a 128-bit random nonce (128 bits) */ uint8_t H0[32]; /**< the hash image H0 - Encrypted - (256 bits) */ uint16_t sig_len; /**< The SAS signature length. If no SAS signature (described in Section 7.2) is present, all bits are set to zero. The signature length is in words and includes the signature type block. If the calculated signature octet count is not a multiple of 4, zeros are added to pad it out to a word boundary. If no signature is present, the overall length of the Confirm1 or Confirm2 message will be set to 19 words - Encrypted - (9 bits) */ uint8_t E; /**< The PBX Enrollment flag (E) is a Boolean bit defined in Section 7.3.1 - Encrypted - (1 bit) */ uint8_t V; /**< The SAS Verified flag (V) is a Boolean bit defined in Section 7.1. - Encrypted - (1 bit) */ uint8_t A; /**< The Allow Clear flag (A) is a Boolean bit defined in Section 4.7.2 - Encrypted - (1 bit) */ uint8_t D; /**< The Disclosure Flag (D) is a Boolean bit defined in Section 11. - Encrypted - (1 bit) */ uint32_t cacheExpirationInterval; /**< The cache expiration interval is defined in Section 4.9 - Encrypted - (32 bits) */ uint8_t signatureBlockType[4]; /**< Optionnal signature type : "PGP " or "X509" string - Encrypted - (32 bits) */ uint8_t *signatureBlock; /**< Optionnal signature block as decribded in section 7.2 - Encrypted - (variable length) */ } bzrtpConfirmMessage_t; /** * @brief Conf2 ACK Message rfc 5.8 * This message contains no data but only a length and message type which are stored in the bzrtpPacket_t structure * There the no need to define a structure type for this packet */ /** * @brief Error Message rfc section 5.9 * The Error message is sent to terminate an in-process ZRTP key agreement exchange due to an error. * There is no need to define a structure for this packet as it contains length and message type which are stored * in the bzrtpPacket_t structure and a 32 bits integer error code only */ /** * @brief Error ACK Message rfc 5.10 * This message contains no data but only a length and message type which are stored in the bzrtpPacket_t structure * There the no need to define a structure type for this packet */ /** * @brief GoClear Message rfc 5.11 * Support for the GoClear message is OPTIONAL in the protocol, and it is sent to switch from SRTP to RTP. */ typedef struct bzrtpGoClearMessage_struct { uint8_t clear_mac[8]; /**< The clear_mac is used to authenticate the GoClear message so that bogus GoClear messages introduced by an attacker can be detected and discarded. (64 bits) */ } bzrtpGoClearMessage_t; /** * * @brief Clear ACK Message rfc 5.12 * This message contains no data but only a length and message type which are stored in the bzrtpPacket_t structure * There the no need to define a structure type for this packet */ /** * @brief SASRelay Message rfc 5.13 * The SASrelay message is sent by a trusted MiTM, most often a PBX. It is not sent as a response to a packet, but is sent as a self-initiated packet by the trusted MiTM (Section 7.3). It can only be sent after the rest of the ZRTP key negotiations have completed, after the Confirm messages and their ACKs. It can only be sent after the trusted MiTM has finished key negotiations with the other party, because it is the other party's SAS that is being relayed. It is sent with retry logic until a RelayACK message (Section 5.14) is received or the retry schedule has been exhausted. Part of the message is encrypted using the negotiated block cipher for media encryption. * Depending on whether the trusted MiTM had taken the role of the initiator or the responder during the ZRTP key negotiation, the * SASrelay message is encrypted with zrtpkeyi or zrtpkeyr. */ typedef struct bzrtpSASRelayMessage_struct { uint8_t MAC[8]; /**< a MAC computed over the encrypted part of the message (64 bits) */ uint8_t CFBIV[16]; /**< The CFB Initialization Vector is a 128-bit random nonce (128 bits) */ uint16_t sig_len; /**< The SAS signature length. The trusted MiTM MAY compute a digital signature on the SAS hash, as described in Section 7.2, using a persistent signing key owned by the trusted MiTM. If no SAS signature is present, all bits are set to zero. The signature length is in words and includes the signature type block. If the calculated signature octet count is not a multiple of 4, zeros are added to pad it out to a word boundary. If no signature block is present, the overall length of the SASrelay message will be set to 19 words.*/ uint8_t V; /**< The SAS Verified flag (V) is a Boolean bit defined in Section 7.1. - Encrypted - (1 bit) */ uint8_t A; /**< The Allow Clear flag (A) is a Boolean bit defined in Section 4.7.2 - Encrypted - (1 bit) */ uint8_t D; /**< The Disclosure Flag (D) is a Boolean bit defined in Section 11. - Encrypted - (1 bit) */ uint8_t renderingScheme[4]; /**< the SAS rendering scheme for the relayed sashash, which will be the same rendering scheme used by the other party on the other side of the trusted MiTM. - Encrypted - (32 bits) */ uint8_t relayedSasHash[32]; /**< the sashash relayed from the other party. The first 32-bit word of the sashash contains the sasvalue, which may be rendered to the user using the specified SAS rendering scheme. If this SASrelay message is being sent to a ZRTP client that does not trust this MiTM, the sashash will be ignored by the recipient and should be set to zeros by the PBX. - Encrypted - (256 bits) */ uint8_t signatureBlockType; /**< Optionnal signature type : "PGP " or "X509" string - Encrypted - (32 bits) */ uint8_t *signatureBlock; /**< Optionnal signature block as decribded in section 7.2 - Encrypted - (variable length) */ } bzrtpSASRelayMessage_t; /** * @brief Relay ACK Message rfc 5.14 * This message contains no data but only a length and message type which are stored in the bzrtpPacket_t structure * There the no need to define a structure type for this packet */ /** * @brief Ping Message * The Ping and PingACK messages are unrelated to the rest of the ZRTP protocol. No ZRTP endpoint is required to generate a Ping message, but every ZRTP endpoint MUST respond to a Ping message with a PingACK message. */ typedef struct bzrtpPingMessage_struct { uint8_t version[4]; /**< a string defining the current version, shall be 1.10 (32 bits) */ uint8_t endpointHash[8]; /**< see section 5.16 for the endpointHash definition (64 bits) */ } bzrtpPingMessage_t; /** * * @brief PingAck Message * The Ping and PingACK messages are unrelated to the rest of the ZRTP protocol. No ZRTP endpoint is required to generate a Ping message, but every ZRTP endpoint MUST respond to a Ping message with a PingACK message. */ typedef struct bzrtpPingAckMessage_struct { uint8_t version[4]; /**< a string defining the current version, shall be 1.10 (32 bits) */ uint8_t endpointHash[8]; /**< see section 5.16 for the endpointHash definition (64 bits) */ uint8_t endpointHashReceived[8]; /**< the endpoint hash received in the ping Message we're acknowledging (64 bits) */ uint32_t SSRC; /**< the SSRC received in the ping packet we're acknowledging (32 bits) */ } bzrtpPingAckMessage_t; /** * @brief Parse a string which shall be a valid ZRTP packet * Check validity and allocate the bzrtpPacket structure but do not parse the message except for type and length. * messageData structure field is not allocated by this function (use then bzrtp_packetParse for that). * The packet check and actual message parsing are split in two functions to avoid useless parsing when message is * to be discarded as the check will give message type (in case of message repetition for example) * * @param[in] input The string buffer storing the complete ZRTP packet * @param[in] inputLength Input length in bytes * @param[in] lastValidSequenceNumber If the sequence number of this packet is smaller than this param, packet will be discarded * and an error code returned * @param[out] exitCode 0 on success, error code otherwise * * @return The create bzrtpPacket structure(to be freed using bzrtp_freeZrtpPacket). NULL on error */ BZRTP_EXPORT bzrtpPacket_t *bzrtp_packetCheck(const uint8_t * input, uint16_t inputLength, uint16_t lastValidSequenceNumber, int *exitCode); /** * @brief Parse the packet to extract the message and allocate the matching message structure if needed * * @param[in] zrtpContext The current ZRTP context, some parameters(key agreement algorithm) may be needed to parse packet. * @param[in] zrtpChannelContext The channel context this packet is intended to(channel context and packet must match peer SSRC). * @param[in] input The string buffer storing the complete ZRTP packet * @param[in] inputLength Input length in bytes * @param[in] zrtpPacket The zrtpPacket structure allocated by previous call to bzrtpPacketCheck * * @return 0 on sucess, error code otherwise */ BZRTP_EXPORT int bzrtp_packetParser(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, const uint8_t * input, uint16_t inputLength, bzrtpPacket_t *zrtpPacket); /** * @brief Create an empty packet and allocate the messageData according to requested packetType * * @param[in] zrtpContext The current ZRTP context, some data (H chain or others, may be needed to create messages) * @param[in] zrtpChannelContext The channel context this packet is intended to * @param[in] messageType The 32bit integer mapped to the message type to be created * @param[out] exitCode 0 on success, error code otherwise * * @return An empty packet initialised to get data for the requested paquet tyep. NULL on error */ BZRTP_EXPORT bzrtpPacket_t *bzrtp_createZrtpPacket(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, uint32_t messageType, int *exitCode); /** * @brief Create a ZRTP packet string from the ZRTP packet values present in the structure * messageType, messageData and sourceIdentifier in zrtpPacket must have been correctly set before calling this function * * @param[in] zrtpContext A zrtp context where to find H0-H3 to compute MAC requested by some paquets or encryption's key for commit/SASRelay packet * @param[in] zrtpChannelContext The channel context this packet is intended to * @param[in/out] zrtpPacket The zrtpPacket structure containing the message Data structure, output is stored in ->packetString * @param[in] sequenceNumber Sequence number of this packet * * @return 0 on success, error code otherwise * */ BZRTP_EXPORT int bzrtp_packetBuild(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket, uint16_t sequenceNumber); /** * @brief Deallocate zrtp Packet * * @param[in] zrtpPacket The packet to be freed * */ BZRTP_EXPORT void bzrtp_freeZrtpPacket(bzrtpPacket_t *zrtpPacket); /** * @brief Modify the current sequence number of the packet in the packetString and sequenceNumber fields * The CRC at the end of packetString is also updated * * param[in/out] zrtpPacket The zrtpPacket to modify, the packetString must have been generated by * a call to bzrtp_packetBuild on this packet * param[in] sequenceNumber The new sequence number to insert in the packetString * * return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_packetUpdateSequenceNumber(bzrtpPacket_t *zrtpPacket, uint16_t sequenceNumber); #endif /* PACKETPARSER_H */ bzrtp-4.4.13/include/stateMachine.h000066400000000000000000000156531364144501400171610ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef STATEMACHINE_H #define STATEMACHINE_H #include "typedef.h" /* types definition for event and state function */ /* the INIT event type is used to run some state for the firt time : create packet and send it */ #define BZRTP_EVENT_INIT 0 #define BZRTP_EVENT_MESSAGE 1 #define BZRTP_EVENT_TIMER 2 /* error code definition */ #define BZRTP_ERROR_UNSUPPORTEDZRTPVERSION 0xe001 #define BZRTP_ERROR_UNMATCHINGPACKETREPETITION 0xe002 #define BZRTP_ERROR_CACHEMISMATCH 0xe004 /** * @brief The event type, used as a parameter for the state function */ typedef struct bzrtpEvent_struct { uint8_t eventType; /**< Event can be a message or a timer's end */ uint8_t *bzrtpPacketString; /**< a pointer to the zrtp packet string, NULL in case of timer event */ uint16_t bzrtpPacketStringLength; /**< the length of packet string in bytes */ bzrtpPacket_t *bzrtpPacket; /**< a pointer to the zrtp packet structure created by the processMessage function */ bzrtpContext_t *zrtpContext; /**< the current ZRTP context */ bzrtpChannelContext_t *zrtpChannelContext; /**< the current ZRTP channel hosting this state machine context */ } bzrtpEvent_t; /** * @brief the state function pointer definition */ typedef int (*bzrtpStateMachine_t)(bzrtpEvent_t); /* state functions prototypes, split in categories corresponding to the differents protocol phases: discovery, key agreement, confirmation */ /** * @brief This is the initial state * On first call, we will create the Hello message and start sending it until we receive an helloACK or a hello message from peer * * Arrives from : * - This is the initial state * Goes to: * - state_discovery_waitingForHello upon HelloACK reception * - state_discovery_waitingForHelloAck upon Hello reception * Send : * - Hello until timer's end or transition */ int state_discovery_init(bzrtpEvent_t event); /** * @brief Arrives in this state coming from init upon reception on Hello ACK, we are now waiting for the Hello packet from peer * * Arrives from : * - state_discovery_init upon HelloACK reception * Goes to: * - state_keyAgreement_sendingCommit upon Hello reception * Send : * - HelloACK on Hello reception * */ int state_discovery_waitingForHello(bzrtpEvent_t event); /** * @brief We are now waiting for the HelloACK packet from peer or a Commit packet * * Arrives from : * - state_discovery_init upon Hello reception * Goes to: * - state_keyAgreement_sendingCommit upon HelloACK reception * - state_keyAgreement_responderSendingDHPart1 upon Commit reception in DHM mode * - state_confirmation_responderSendingConfirm1 upon Commit reception in non DHM mode * Send : * - Hello until timer's end or transition * - HelloACK on Hello reception * */ int state_discovery_waitingForHelloAck(bzrtpEvent_t event); /** * @brief For any kind of key agreement (DHM, Mult, PreShared), we keep sending commit. * * Arrives from : * - state_discovery_waitingForHello upon Hello received * - state_discovery_waitingForHelloAck upon HelloACK received * Goes to: * - state_keyAgreement_initiatorSendingDHPart2 upon DHPart1 reception in DHM mode * - state_confirmation_initiatorSendingConfirm2 upon Confirm1 reception in non DHM mode * - state_keyAgreement_responderSendingDHPart1 upon Commit reception in DHM mode and commit contention gives us the responder role * - state_confirmation_responderSendingConfirm1 upon Commit reception in non DHM mode and commit contention gives us the responder role * Send : * - Commit until timer's end or transition * - HelloACK on Hello reception * */ int state_keyAgreement_sendingCommit(bzrtpEvent_t event); /** * @brief For DHM mode only, responder send DHPart1 packet * * Arrives from: * - state_discovery_waitingForHelloAck upon Commit reception in DHM mode * - state_keyAgreement_sendingCommit upon Commit reception in DHM mode and commit contention gives us the responder role * Goes to: * - state_confirmation_responderSendingConfirm1 upon DHPart2 reception * Send : * - DHPart1 on Commit reception * */ int state_keyAgreement_responderSendingDHPart1(bzrtpEvent_t event); /** * @brief For DHM mode only, initiator send DHPart2 packet * * Arrives from: * - state_keyAgreement_sendingCommit upon DHPart1 reception * Goes to: * - state_confirmation_initiatorSendingConfirm2 upon reception of Confirm1 * Send : * - DHPart2 until timer's end or transition * */ int state_keyAgreement_initiatorSendingDHPart2(bzrtpEvent_t event); /** * @brief Responder send the confirm1 message * * Arrives from: * - state_keyAgreement_responderSendingDHPart1 upon DHPart2 reception * - state_keyAgreement_sendingCommit upon Commit reception in non DHM mode and commit contention gives us the responder role * - state_discovery_waitingForHelloAck upon Commit reception in non DHM mode * Goes to: * - state_secure on Confirm2 reception * Send : * - Confirm1 on Commit or DHPart2 reception * */ int state_confirmation_responderSendingConfirm1(bzrtpEvent_t event); /** * @brief Initiator send the confirm2 message * * Arrives from: * - state_keyAgreement_initiatorSendingDHPart2 upon confirm1 reception * - state_keyAgreement_sendingCommit upon Confirm1 reception in non DHM mode * Goes to: * - state_secure on Conf2ACK reception or first SRTP message * Send : * - Confirm2 until timer's end or transition * */ int state_confirmation_initiatorSendingConfirm2(bzrtpEvent_t event); /** * @brief We are in secure state * * Arrives from: * - state_confirmation_responderSendingConfirm1 on Confirm2 reception * - state_confirmation_initiatorSendingConfirm2 on conf2ACK or first SRTP message * Goes to: * - This is the end(we do not support GoClear message), state machine may be destroyed after going to secure mode * Send : * - Conf2ACK on Confirm2 reception * */ int state_secure(bzrtpEvent_t event); /** * @brief Compute the new rs1 and update the cached secrets according to rfc section 4.6.1 * * param[in] zrtpContext The context we are operation on * param[in/out] zrtpChannelContext The channel context we are operation on(contains s0) * * return 0 on success, error code otherwise */ int bzrtp_updateCachedSecrets(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); #endif /* STATEMACHINE_H */ bzrtp-4.4.13/include/typedef.h000066400000000000000000000364671364144501400162220ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TYPEDEF_H #define TYPEDEF_H /* maximum number of simultaneous channels opened in a ZRTP session */ #define ZRTP_MAX_CHANNEL_NUMBER 2 /* aux secret may rarely be used define his maximum length in bytes */ #define MAX_AUX_SECRET_LENGTH 64 /* the context will store some of the sent or received packets */ #define PACKET_STORAGE_CAPACITY 4 #define HELLO_MESSAGE_STORE_ID 0 #define COMMIT_MESSAGE_STORE_ID 1 #define DHPART_MESSAGE_STORE_ID 2 #define CONFIRM_MESSAGE_STORE_ID 3 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef ZIDCACHE_ENABLED #include "sqlite3.h" #endif /* ZIDCACHE_ENABLED */ typedef struct bzrtpChannelContext_struct bzrtpChannelContext_t; #include #include #include "packetParser.h" #include "stateMachine.h" /* logging */ /* log domain is defined in CMakeList */ #include "bctoolbox/logging.h" #ifdef _WIN32 #define snprintf _snprintf #endif /* timer related definitions */ #define BZRTP_TIMER_ON 1 #define BZRTP_TIMER_OFF 2 /* values for retransmission timers, as recommended in rfc section 6 */ #define HELLO_BASE_RETRANSMISSION_STEP 50 #define HELLO_CAP_RETRANSMISSION_STEP 200 #define HELLO_MAX_RETRANSMISSION_NUMBER 20 #define NON_HELLO_BASE_RETRANSMISSION_STEP 150 #define NON_HELLO_CAP_RETRANSMISSION_STEP 1200 #define NON_HELLO_MAX_RETRANSMISSION_NUMBER 10 /* Client identifier can contain up to 16 characters, it identify the BZRTP library version */ /* Use it to pass bzrtp version number to peer, is it part of Hello message */ /* custom Linphone Instant Messaging Encryption depends on bzrtp version */ /* Note: ZRTP_VERSION and BZrtp version are for now both at 1.1 but they are unrelated */ /* historically since the creation of bzrtp, it used client idenfiers : */ #define ZRTP_CLIENT_IDENTIFIERv1_0a "LINPHONE-ZRTPCPP" #define ZRTP_CLIENT_IDENTIFIERv1_0b "BZRTP" /* Since version 1.1 which implement correctly the key export mechanism described in ZRTP RFC 4.5.2, bzrtp lib identifies itself as */ #define ZRTP_CLIENT_IDENTIFIERv1_1 "BZRTPv1.1" #define ZRTP_CLIENT_IDENTIFIER ZRTP_CLIENT_IDENTIFIERv1_1 /* pgp word list for use with SAS */ extern const char * pgpWordsEven[]; extern const char * pgpWordsOdd[]; /** * @brief Timer structure : The timer mechanism receives a tick giving a current time in ms * a timer object will check on tick reception if it must fire or not */ typedef struct bzrtpTimer_struct { uint8_t status; /**< Status is BZRTP_TIMER_ON or BZRTP_TIMER_OFF */ uint64_t firingTime; /**< in ms. The timer will fire if currentTime >= firingTime */ uint8_t firingCount; /**< Timer is used to resend packets, count the number of times a packet has been resent */ int timerStep; /**< in ms. Step between next timer fire: used to reset firingTime for next timer fire */ } bzrtpTimer_t; /* the rs1 and rs2 are 256 bits long - see rfc section 4.6.1 */ #define RETAINED_SECRET_LENGTH 32 /** * @brief A set of cached secrets retrieved from the cache as defined */ typedef struct cachedSecrets_struct { uint8_t *rs1; /**< retained secret 1 */ uint8_t rs1Length; /**< retained secret 1 length in bytes */ uint8_t *rs2; /**< retained secret 2 */ uint8_t rs2Length; /**< retained secret 2 length in bytes */ uint8_t *auxsecret; /**< auxiliary secret */ uint8_t auxsecretLength; /**< auxiliary secret length in bytes */ uint8_t *pbxsecret; /**< PBX secret */ uint8_t pbxsecretLength; /**< PBX secret length in bytes */ uint8_t previouslyVerifiedSas; /* boolean, is a SAS has been previously verified with this user */ } cachedSecrets_t; /** * @brief The hash of cached secret truncated to the 64 leftmost bits * aux secret ID is not part of it because channel context dependend while these one are session wise */ typedef struct cachedSecretsHash_struct { uint8_t rs1ID[8]; /**< retained secret 1 Hash */ uint8_t rs2ID[8]; /**< retained secret 2 Hash */ uint8_t pbxsecretID[8]; /**< pbx secret Hash */ } cachedSecretsHash_t; /** * @brief The zrtp context of a channel * */ struct bzrtpChannelContext_struct { void *clientData; /**< this is a pointer provided by the client which is then resent as a parameter of the callbacks functions. Usefull to store RTP session context for example */ uint8_t role;/**< can be INITIATOR or RESPONDER, is set to INITIATOR at creation, may switch to responder later */ bzrtpStateMachine_t stateMachine; /**< The state machine function, holds the current state of the channel: points to the current state function */ bzrtpTimer_t timer; /**< a timer used to manage packets retransmission */ uint32_t selfSSRC; /**< A context is identified by his own SSRC and the peer one */ /* flags */ uint8_t isSecure; /**< This flag is set to 1 when the ZRTP negociation ends and SRTP secrets are generated and confirmed for this channel */ uint8_t isMainChannel; /**< this flag is set for the firt channel only, allow to distinguish channel to be secured using DHM or multiStream */ /* Hash chains, self is generated at channel context init */ uint8_t selfH[4][32]; /**< Store self 256 bits Hash images H0-H3 used to generate messages MAC */ uint8_t peerH[4][32]; /**< Store peer 256 bits Hash images H0-H3 used to check messages authenticity */ /* packet storage : shall store some sent and received packets */ bzrtpPacket_t *selfPackets[PACKET_STORAGE_CAPACITY]; /**< Hello, Commit and DHPart packet locally generated */ bzrtpPacket_t *peerPackets[PACKET_STORAGE_CAPACITY]; /**< Hello, Commit and DHPart packet received from peer */ /* peer Hello hash : store the peer hello hash when given by signaling */ uint8_t *peerHelloHash; /**< peer hello hash - SHA256 of peer Hello packet, given through signaling, shall be a 32 bytes buffer */ /* sequence number: self and peer */ uint16_t selfSequenceNumber; /**< Sequence number of the next packet to be sent */ uint16_t peerSequenceNumber; /**< Sequence number of the last valid received packet */ /* algorithm agreed after Hello message exchange(use mapping define in cryptoUtils.h) and the function pointer to use them */ uint8_t hashAlgo; /**< hash algorithm agreed on after Hello packet exchange, stored using integer mapping defined in cryptoUtils.h */ uint8_t hashLength; /**< the length in bytes of a hash generated with the agreed hash algo */ uint8_t cipherAlgo; /**< cipher algorithm agreed on after Hello packet exchange, stored using integer mapping defined in cryptoUtils.h */ uint8_t cipherKeyLength; /**< the length in bytes of the key needed by the agreed cipher block algo */ uint8_t authTagAlgo; /**< srtp authentication tag algorithm agreed on after Hello packet exchange, stored using integer mapping defined in cryptoUtils.h */ uint8_t keyAgreementAlgo; /**< key agreement algorithm agreed on after Hello packet exchange, stored using integer mapping defined in cryptoUtils.h */ uint16_t keyAgreementLength; /**< the length in byte of the secret generated by the agreed key exchange algo */ uint8_t sasAlgo; /**< sas rendering algorithm agreed on after Hello packet exchange, stored using integer mapping defined in cryptoUtils.h */ uint8_t sasLength; /**< length of the SAS depends on the algorithm agreed */ /* function pointer to the agreed algorithms - Note, key agreement manage directly this selection so it is not set here */ void (*hmacFunction)(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output); /**< function pointer to the agreed hmacFunction */ void (*hashFunction)(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output); /**< function pointer to the agreed hash function */ void (*cipherEncryptionFunction)(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /**< function pointer to the agreed cipher block function, encryption mode */ void (*cipherDecryptionFunction)(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /**< function pointer to the agreed cipher block function, decryption mode */ void (*sasFunction)(uint32_t sas, char * output, int outputSize); /**< function pointer to the agreed sas rendering function */ /* keys */ uint8_t *s0; /**< the s0 as describred rfc section 4.4 - have a length of hashLength */ uint8_t *KDFContext; /**< defined in rfc section 4.4 */ uint16_t KDFContextLength; /**< length of the KDF context, is 24 + output length of the selected hash algo */ uint8_t *mackeyi; /**< the initiator mackey as defined in rfc section 4.5.3 - have a length of hashLength */ uint8_t *mackeyr; /**< the responder mackey as defined in rfc section 4.5.3 - have a length of hashLength*/ uint8_t *zrtpkeyi; /**< the initiator mackey as defined in rfc section 4.5.3 - have a length of cipherKeyLength */ uint8_t *zrtpkeyr; /**< the responder mackey as defined in rfc section 4.5.3 - have a length of cipherKeyLength*/ bzrtpSrtpSecrets_t srtpSecrets; /**< the secrets keys and salt needed by SRTP */ /* shared secret hash : unlike pbx, rs1 and rs2 secret hash, the auxsecret hash use a channel dependent data (H3) and is then stored in the channel context */ uint8_t initiatorAuxsecretID[8]; /**< initiator auxiliary secret Hash */ uint8_t responderAuxsecretID[8]; /**< responder auxiliary secret Hash */ /* temporary buffer stored in the channel context */ bzrtpPacket_t *pingPacket; /**< Temporary stores a ping packet when received to be used to create the pingACK response */ }; /** * @brief structure of the ZRTP engine context * Store current state, timers, HMAC and encryption keys */ struct bzrtpContext_struct { /* contexts */ bctbx_rng_context_t *RNGContext; /**< context for random number generation */ void *keyAgreementContext; /**< context for the key agreement operations. Only one key agreement computation may be done during a call, so this belongs to the general context and not the channel one */ uint8_t keyAgreementAlgo; /**< key agreement algorithm agreed on the first channel, the one performing key exchange, stored using integer mapping defined in cryptoUtils.h, */ /* flags */ uint8_t isInitialised; /**< this flag is set once the context was initialised : self ZID retrieved from cache or generated, used to unlock the creation of addtional channels */ uint8_t isSecure; /**< this flag is set to 1 after the first channel have completed the ZRTP protocol exchange(i.e. when the responder have sent the conf2ACK message), must be set in order to start an additional channel */ uint8_t peerSupportMultiChannel; /**< this flag is set to 1 when the first valid HELLO packet from peer arrives if it support Multichannel ZRTP */ uint64_t timeReference; /**< in ms. This field will set at each channel State Machine start and updated at each tick after creation of the context, it is used to set the firing time of a channel timer */ /* callbacks */ bzrtpCallbacks_t zrtpCallbacks; /**< structure holding all the pointers to callbacks functions needed by the ZRTP engine. Functions are set by client using the bzrtp_setCallback function */ /* channel contexts */ bzrtpChannelContext_t *channelContext[ZRTP_MAX_CHANNEL_NUMBER]; /**< All the context data needed for a channel are stored in a dedicated structure */ /* List of available algorithms, initialised with algo implemented in cryptoWrapper but can be then be modified according to user settings */ uint8_t hc; /**< hash count -zrtpPacket set to 0 means we support only HMAC-SHA256 (4 bits) */ uint8_t supportedHash[7]; /**< list of supported hash algorithms mapped to uint8_t */ uint8_t cc; /**< cipher count - set to 0 means we support only AES128-CFB128 (4 bits) */ uint8_t supportedCipher[7]; /**< list of supported cipher algorithms mapped to uint8_t */ uint8_t ac; /**< auth tag count - set to 0 mean we support only HMAC-SHA1-32 (4 bits) */ uint8_t supportedAuthTag[7]; /**< list of supported SRTP authentication tag algorithms mapped to uint8_t */ uint8_t kc; /**< key agreement count - set to 0 means we support only Diffie-Hellman-Merkle 3072 (4 bits) */ uint8_t supportedKeyAgreement[7]; /**< list of supported key agreement algorithms mapped to uint8_t */ uint8_t sc; /**< sas count - set to 0 means we support only base32 (4 bits) */ uint8_t supportedSas[7]; /**< list of supported Sas representations mapped to uint8_t */ /* ZIDs and cache */ #ifdef ZIDCACHE_ENABLED sqlite3 *zidCache; /**< an sqlite3 db pointer to the zid cache **/ #else void *zidCache; /**< an empty pointer always set to NULL when cache is disabled **/ #endif /* ZIDCACHE_ENABLED */ bctbx_mutex_t *zidCacheMutex; /**< lock access to the cache if provided **/ int zuid; /**< internal id used to address zid cache SIP/ZID pair binding **/ char *selfURI; /**< a null terminated string storing the local user URI **/ uint8_t selfZID[12]; /**< The ZRTP Identifier of this ZRTP end point - a random if running cache less */ char *peerURI; /**< a null terminated string storing the peer user URI **/ uint8_t peerZID[12]; /**< The ZRTP Identifier of the peer ZRTP end point - given by the Hello packet */ uint32_t peerBzrtpVersion; /**< The Bzrtp library version used by peer, retrieved from the peer Hello packet Client identifier and used for backward compatibility in exported key computation */ cachedSecrets_t cachedSecret; /**< the local cached secrets */ cachedSecretsHash_t initiatorCachedSecretHash; /**< The hash of cached secret from initiator side, computed as described in rfc section 4.3.1 */ cachedSecretsHash_t responderCachedSecretHash; /**< The hash of cached secret from responder side, computed as described in rfc section 4.3.1 */ uint8_t cacheMismatchFlag; /**< Flag set in case of cache mismatch(detected in DHM mode when DH part packet arrives) */ uint8_t peerPVS; /**< used to store value of PVS flag sent by peer in the confirm packet on first channel only, then used to compute the PVS value sent to the application */ /* transient auxiliary shared secret : in addition to the auxiliary shared secret stored in ZID cache, caller can provide a shared secret to the zrtp context which will be used for this transaction only */ /* both auxiliary secret are used and combined as transientAuxiliarySecret appended to cachedAuxiliarySecret*/ uint8_t *transientAuxSecret; /**< an auxiliary secret not stored in cache, provided after context creation and before the main channel is started */ size_t transientAuxSecretLength; /**< size of the previous buffer */ /* keys */ uint8_t *ZRTPSess; /**< ZRTP session key as described in rfc section 4.5.2 */ uint8_t ZRTPSessLength; /**< length of ZRTP session key depends on agreed hash algorithm */ uint8_t *exportedKey; /**< computed as in rfc section 4.5.2 only if needed */ uint8_t exportedKeyLength; /**< length of previous buffer, shall be channel[0]->hashLength */ }; #endif /* ifndef TYPEDEF_H */ bzrtp-4.4.13/include/zidCache.h000066400000000000000000000105661364144501400162640ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ZIDCACHE_H #define ZIDCACHE_H #include "typedef.h" /** * @brief Parse the cache to find secrets associated to the given ZID, set them and their length in the context if they are found * Note: this function also retrieve zuid(set in the context) wich allow successive calls to cache operation to be faster. * * @param[in/out] context the current context, used to get the cache db pointer, self and peer URI and store results * @param[in] peerZID a byte array of the peer ZID * * return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]); /** * @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID. * Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed and requested * Any pair ZID/sipURI shall identify an account on a device. * * @param[in/out] db the opened sqlite database pointer * @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI) * @param[in] peerURI peer URI * @param[in] peerZID peer ZID * @param[in] insertFlag A boolean managing insertion or not of a new row: * - BZRTP_ZIDCACHE_DONT_INSERT_ZUID : if not found identity binding won't lead to insertion and return zuid will be 0 * - BZRTP_ZIDCACHE_INSERT_ZUID : if not found, insert a new row in ziduri table and return newly inserted zuid * @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant * if identity binding is not found and insertFlag set to 0, this value is set to 0 * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on success, BZRTP_ERROR_CACHE_PEERNOTFOUND if peer was not in and the insert flag is not set to BZRTP_ZIDCACHE_INSERT_ZUID, error code otherwise */ #define BZRTP_ZIDCACHE_DONT_INSERT_ZUID 0 #define BZRTP_ZIDCACHE_INSERT_ZUID 1 BZRTP_EXPORT int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], const uint8_t insertFlag, int *zuid, bctbx_mutex_t *zidCacheMutex); /** * @brief This is a convenience wrapper to the bzrtp_cache_write function which will also take care of * setting the ziduri table 'active' flag to one for the current row and reset all other rows with matching peeruri * * Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be inserted, lengths of theses values * All three arrays must be the same lenght: columnsCount * If the row isn't present in the given table, it will be inserted * * @param[in/out] context the current context, used to get the cache db pointer, zuid and cache mutex * @param[in] tableName The name of the table to write in the db, must already exists. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to update * @param[in] values An array of buffers containing the values to insert/update matching the order of columns array * @param[in] lengths An array of integer containing the lengths of values array buffer matching the order of columns array * @param[in] columnsCount length common to columns,values and lengths arrays * * @return 0 on succes, error code otherwise */ BZRTP_EXPORT int bzrtp_cache_write_active(bzrtpContext_t *context, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount); #endif /* ZIDCACHE_H */ bzrtp-4.4.13/libbzrtp.pc.in000066400000000000000000000004371364144501400155330ustar00rootroot00000000000000# This is a comment prefix=@prefix@ exec_prefix=@exec_prefix@ includedir=@includedir@ Name: libbzrtp Description: Implement the ZRTP Media Path Key agreement for unicast secure RTP Version: @PACKAGE_VERSION@ Libs: -L@libdir@ -lbzrtp Cflags: -I@includedir@ bzrtp-4.4.13/src/000077500000000000000000000000001364144501400135355ustar00rootroot00000000000000bzrtp-4.4.13/src/CMakeLists.txt000066400000000000000000000063101364144501400162750ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ set(SOURCE_FILES bzrtp.c cryptoUtils.c packetParser.c pgpwords.c stateMachine.c zidCache.c ) add_definitions( -DBCTBX_LOG_DOMAIN="bzrtp" ) if(POLARSSL_FOUND) list(APPEND SOURCE_FILES cryptoPolarssl.c) elseif(MBEDTLS_FOUND) list(APPEND SOURCE_FILES cryptoMbedtls.c) endif() bc_apply_compile_flags(SOURCE_FILES STRICT_OPTIONS_CPP) set(INCLUDE_DIRS ) set(LIBS ) if(SQLITE3_FOUND) list(APPEND INCLUDE_DIRS ${SQLITE3_INCLUDE_DIRS}) list(APPEND LIBS $ $) endif() if(XML2_FOUND) list(APPEND INCLUDE_DIRS ${XML2_INCLUDE_DIRS}) list(APPEND LIBS $ $) endif() if(ENABLE_STATIC) add_library(bzrtp STATIC ${SOURCE_FILES}) set_target_properties(bzrtp PROPERTIES OUTPUT_NAME bzrtp) target_include_directories(bzrtp INTERFACE $ PRIVATE ${INCLUDE_DIRS}) target_link_libraries(bzrtp PUBLIC bctoolbox ${LIBS}) install(TARGETS bzrtp EXPORT ${EXPORT_TARGETS_NAME}Targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() if(ENABLE_SHARED) add_library(bzrtp SHARED ${SOURCE_FILES}) target_compile_definitions(bzrtp PRIVATE "-DBZRTP_EXPORTS") set_target_properties(bzrtp PROPERTIES VERSION 0) target_include_directories(bzrtp INTERFACE $ $ PRIVATE ${INCLUDE_DIRS} ) target_link_libraries(bzrtp PUBLIC bctoolbox ${LIBS}) if(MSVC) if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/bzrtp.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() endif() install(TARGETS bzrtp EXPORT ${EXPORT_TARGETS_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() bzrtp-4.4.13/src/Makefile.am000066400000000000000000000005551364144501400155760ustar00rootroot00000000000000lib_LTLIBRARIES = libbzrtp.la libbzrtp_la_LIBADD= $(SQLITE3_LIBS) $(LIBXML2_LIBS) $(BCTOOLBOX_LIBS) libbzrtp_la_SOURCES= bzrtp.c cryptoUtils.c packetParser.c zidCache.c stateMachine.c pgpwords.c AM_CPPFLAGS= -I$(top_srcdir)/include AM_CFLAGS= $(LIBXML2_CFLAGS) $(LIBSQLITE3_CFLAGS) $(BCTOOLBOX_CFLAGS) libbzrtp_la_LDFLAGS=-fvisibility=hidden -no-undefined bzrtp-4.4.13/src/bzrtp.c000066400000000000000000001470001364144501400150440ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "bzrtp/bzrtp.h" #include "typedef.h" #include "bctoolbox/crypto.h" #include "cryptoUtils.h" #include "zidCache.h" #include "packetParser.h" #include "stateMachine.h" #define BZRTP_ERROR_INVALIDCHANNELCONTEXT 0x8001 /* local functions prototypes */ static int bzrtp_initChannelContext(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, uint32_t selfSSRC, uint8_t isMain); static void bzrtp_destroyChannelContext(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); static bzrtpChannelContext_t *getChannelContext(bzrtpContext_t *zrtpContext, uint32_t selfSSRC); static uint8_t copyCryptoTypes(uint8_t destination[7], uint8_t source[7], uint8_t size); /* * Create context structure and initialise it * * @return The ZRTP engine context data * */ bzrtpContext_t *bzrtp_createBzrtpContext(void) { int i; /*** create and intialise the context structure ***/ bzrtpContext_t *context = malloc(sizeof(bzrtpContext_t)); memset(context, 0, sizeof(bzrtpContext_t)); /* start the random number generator */ context->RNGContext = bctbx_rng_context_new(); /* TODO: give a seed for the RNG? */ /* set the DHM context to NULL, it will be created if needed when creating a DHPart packet */ context->keyAgreementContext = NULL; context->keyAgreementAlgo = ZRTP_UNSET_ALGO; /* set flags */ context->isSecure = 0; /* start unsecure */ context->peerSupportMultiChannel = 0; /* peer does not support Multichannel by default */ context->isInitialised = 0; /* will be set by bzrtp_initBzrtpContext */ /* set to NULL all callbacks pointer */ context->zrtpCallbacks.bzrtp_statusMessage = NULL; context->zrtpCallbacks.bzrtp_sendData = NULL; context->zrtpCallbacks.bzrtp_srtpSecretsAvailable = NULL; context->zrtpCallbacks.bzrtp_startSrtpSession = NULL; context->zrtpCallbacks.bzrtp_contextReadyForExportedKeys = NULL; for (i=1; ichannelContext[i] = NULL; } /* get the list of crypto algorithms provided by the crypto module */ /* this list may then be updated according to users settings */ context->hc = bzrtpUtils_getAvailableCryptoTypes(ZRTP_HASH_TYPE, context->supportedHash); context->cc = bzrtpUtils_getAvailableCryptoTypes(ZRTP_CIPHERBLOCK_TYPE, context->supportedCipher); context->ac = bzrtpUtils_getAvailableCryptoTypes(ZRTP_AUTHTAG_TYPE, context->supportedAuthTag); context->kc = bzrtpUtils_getAvailableCryptoTypes(ZRTP_KEYAGREEMENT_TYPE, context->supportedKeyAgreement); context->sc = bzrtpUtils_getAvailableCryptoTypes(ZRTP_SAS_TYPE, context->supportedSas); /* initialise cached secret buffer to null */ context->zidCache = NULL; /* a pointer to the sqlite3 db accessor, can be NULL if running cacheless */ context->zidCacheMutex = NULL; /* a pointer to a mutex provided by the environment to lock database during access, ignored if NULL */ context->zuid = 0; context->peerBzrtpVersion = 0; context->selfURI = NULL; context->cachedSecret.rs1 = NULL; context->cachedSecret.rs1Length = 0; context->cachedSecret.rs2 = NULL; context->cachedSecret.rs2Length = 0; context->cachedSecret.pbxsecret = NULL; context->cachedSecret.pbxsecretLength = 0; context->cachedSecret.auxsecret = NULL; context->cachedSecret.auxsecretLength = 0; context->cacheMismatchFlag = 0; context->peerPVS = 0; /* initialise transient shared auxiliary secret buffer */ context->transientAuxSecret = NULL; context->transientAuxSecretLength = 0; /* initialise key buffers */ context->ZRTPSess = NULL; context->ZRTPSessLength = 0; context->exportedKey = NULL; context->exportedKeyLength = 0; return context; } /** * @brief Set the pointer allowing cache access * * @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled * @param[in] selfURI Local URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] peerURI Peer URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * * @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise */ int bzrtp_setZIDCache(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI) { #ifdef ZIDCACHE_ENABLED /* is zrtp context valid */ if (context==NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* zidCache pointer is actually a pointer to sqlite3 db, store it in context */ context->zidCache = (sqlite3 *)zidCache; if (context->selfURI != NULL) { free(context->selfURI); } context->selfURI = strdup(selfURI); if (context->peerURI != NULL) { free(context->peerURI); } context->peerURI = strdup(peerURI); /* and init the cache(create needed tables if they don't exist) */ return bzrtp_initCache_lock(context->zidCache, context->zidCacheMutex); #else /* ZIDCACHE_ENABLED */ return BZRTP_ERROR_CACHEDISABLED; #endif /* ZIDCACHE_ENABLED */ } /** * @brief Set the pointer allowing cache access, this version of the function get a mutex to lock the cache when accessing it * * Note: bzrtp does not manage the given mutex pointer, it has to be already initialized * and shall be destroyed by environment after the BZRTP session is completed. * * @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled * @param[in] selfURI Local URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] peerURI Peer URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access. Can be NULL, the zidcache is then non locked. * * @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise */ int bzrtp_setZIDCache_lock(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI, bctbx_mutex_t *zidCacheMutex) { #ifdef ZIDCACHE_ENABLED /* is zrtp context valid */ if (context==NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } context->zidCacheMutex = zidCacheMutex; /* have the non lockable function finish the job */ return bzrtp_setZIDCache(context, zidCache, selfURI, peerURI); #else /* ZIDCACHE_ENABLED */ return BZRTP_ERROR_CACHEDISABLED; #endif /* ZIDCACHE_ENABLED */ } /** * @brief Perform some initialisation which can't be done without some callback functions: * This function is called once per session when the first channel is created. * It must be called after the cache access pointer have been set * - Get ZID from cache or generate a random ZID * - Initialise the first channel * * @param[in] context The context to initialise * @param[in] selfSSRC SSRC of the first channel * @return 0 on success */ int bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) { /* is zrtp context valid */ if (context==NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* initialise self ZID. Randomly generated if no ZID is found in cache or no cache found */ bzrtp_getSelfZID_lock(context->zidCache, context->selfURI, context->selfZID, context->RNGContext, context->zidCacheMutex); context->isInitialised = 1; /* allocate 1 channel context, set all the others pointers to NULL */ context->channelContext[0] = (bzrtpChannelContext_t *)malloc(sizeof(bzrtpChannelContext_t)); memset(context->channelContext[0], 0, sizeof(bzrtpChannelContext_t)); return bzrtp_initChannelContext(context, context->channelContext[0], selfSSRC, 1); } /* * Free memory of context structure to a channel, if all channels are freed, free the global zrtp context * @param[in] context Context hosting the channel to be destroyed.(note: the context zrtp context itself is destroyed with the last channel) * @param[in] selfSSRC The SSRC identifying the channel to be destroyed * * @return the number of channel still active in this ZRTP context * */ int bzrtp_destroyBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) { int i; int validChannelsNumber = 0; if (context == NULL) { return 0; } /* Find the channel to be destroyed, destroy it and check if we have anymore valid channels */ for (i=0; ichannelContext[i] != NULL) { if (context->channelContext[i]->selfSSRC == selfSSRC) { bzrtp_destroyChannelContext(context, context->channelContext[i]); context->channelContext[i] = NULL; } else { validChannelsNumber++; } } } if (validChannelsNumber>0) { return validChannelsNumber; /* we have more valid channels, keep the zrtp context */ } /* We have no more channel, destroy the zrtp context */ /* key agreement context shall already been destroyed after s0 computation, but just in case */ if (context->keyAgreementContext != NULL) { if (context->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || context->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_DestroyECDHContext((bctbx_ECDHContext_t *)context->keyAgreementContext); } if (context->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || context->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DestroyDHMContext((bctbx_DHMContext_t *)context->keyAgreementContext); } context->keyAgreementContext = NULL; context->keyAgreementAlgo = ZRTP_UNSET_ALGO; } /* Destroy keys and secrets */ /* rs1, rs2, pbxsecret and auxsecret shall already been destroyed, just in case */ if (context->cachedSecret.rs1!=NULL) { bzrtp_DestroyKey(context->cachedSecret.rs1, context->cachedSecret.rs1Length, context->RNGContext); free(context->cachedSecret.rs1); context->cachedSecret.rs1 = NULL; } if (context->cachedSecret.rs2!=NULL) { bzrtp_DestroyKey(context->cachedSecret.rs2, context->cachedSecret.rs2Length, context->RNGContext); free(context->cachedSecret.rs2); context->cachedSecret.rs2 = NULL; } if (context->cachedSecret.auxsecret!=NULL) { bzrtp_DestroyKey(context->cachedSecret.auxsecret, context->cachedSecret.auxsecretLength, context->RNGContext); free(context->cachedSecret.auxsecret); context->cachedSecret.auxsecret = NULL; } if (context->cachedSecret.pbxsecret!=NULL) { bzrtp_DestroyKey(context->cachedSecret.pbxsecret, context->cachedSecret.pbxsecretLength, context->RNGContext); free(context->cachedSecret.pbxsecret); context->cachedSecret.pbxsecret = NULL; } if (context->ZRTPSess!=NULL) { bzrtp_DestroyKey(context->ZRTPSess, context->ZRTPSessLength, context->RNGContext); free(context->ZRTPSess); context->ZRTPSess=NULL; } if (context->exportedKey!=NULL) { bzrtp_DestroyKey(context->exportedKey, context->exportedKeyLength, context->RNGContext); free(context->exportedKey); context->ZRTPSess=NULL; } free(context->selfURI); free(context->peerURI); /* transient shared auxiliary secret */ if (context->transientAuxSecret != NULL) { bzrtp_DestroyKey(context->transientAuxSecret, context->transientAuxSecretLength, context->RNGContext); free(context->transientAuxSecret); context->transientAuxSecret=NULL; } /* destroy the RNG context at the end because it may be needed to destroy some keys */ bctbx_rng_context_free(context->RNGContext); context->RNGContext = NULL; free(context); return 0; } /* * @brief Allocate a function pointer to the callback function identified by his id * @param[in/out] context The zrtp context to set the callback function * @param[in] cbs A structure containing all the callbacks to supply. * * @return 0 on success */ int bzrtp_setCallbacks(bzrtpContext_t *context, const bzrtpCallbacks_t *cbs) { /* is zrtp context valid */ if (context==NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } context->zrtpCallbacks=*cbs; return 0; } /** * @brief Add a channel to an existing context, this can be done only if the first channel has concluded a DH key agreement * * @param[in/out] zrtpContext The zrtp context who will get the additionnal channel. Must be in secure state. * @param[in] selfSSRC The SSRC given to the channel context * * @return 0 on succes, error code otherwise */ int bzrtp_addChannel(bzrtpContext_t *zrtpContext, uint32_t selfSSRC) { bzrtpChannelContext_t *zrtpChannelContext = NULL; int i=0; /* is zrtp context valid */ if (zrtpContext==NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* context must be initialised(selfZID available) to enable the creation of an additional channel */ if (zrtpContext->isInitialised == 0) { return BZRTP_ERROR_CONTEXTNOTREADY; } /* get the first free channel context from ZRTP context and create a channel context */ while(ichannelContext[i] == NULL) { int retval; zrtpChannelContext = (bzrtpChannelContext_t *)malloc(sizeof(bzrtpChannelContext_t)); memset(zrtpChannelContext, 0, sizeof(bzrtpChannelContext_t)); retval = bzrtp_initChannelContext(zrtpContext, zrtpChannelContext, selfSSRC, 0); if (retval != 0) { free(zrtpChannelContext); return retval; } } else { i++; } } if (zrtpChannelContext == NULL) { return BZRTP_ERROR_UNABLETOADDCHANNEL; } /* attach the created channel to the ZRTP context */ zrtpContext->channelContext[i] = zrtpChannelContext; return 0; } /* * @brief Start the state machine of the specified channel * * @param[in/out] zrtpContext The ZRTP context hosting the channel to be started * @param[in] selfSSRC The SSRC identifying the channel to be started(will start sending Hello packets and listening for some) * * @return 0 on succes, error code otherwise */ int bzrtp_startChannelEngine(bzrtpContext_t *zrtpContext, uint32_t selfSSRC) { bzrtpEvent_t initEvent; /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* is this channel already started? */ if (zrtpChannelContext->stateMachine != NULL) { return BZRTP_ERROR_CHANNELALREADYSTARTED; } /* if this is an additional channel(not channel 0), we must be sure that channel 0 is already secured */ if (zrtpChannelContext->isMainChannel == 0) { /* is ZRTP context able to add a channel (means channel 0 has already performed the secrets generation) */ if (zrtpContext->isSecure == 0) { return BZRTP_ERROR_CONTEXTNOTREADY; } /* check the peer support Multichannel(shall be set in the first Hello message received) */ if (zrtpContext->peerSupportMultiChannel == 0) { return BZRTP_ERROR_MULTICHANNELNOTSUPPORTEDBYPEER; } } /* set the timer reference to 0 to force a message to be sent at first timer tick */ zrtpContext->timeReference = 0; /* start the engine by setting the state to init and calling it */ zrtpChannelContext->stateMachine = state_discovery_init; /* create an INIT event to call the init state function which will create a hello packet and start sending it */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; return zrtpChannelContext->stateMachine(initEvent); } /* * @brief Send the current time to a specified channel, it will check if it has to trig some timer * * @param[in/out] zrtpContext The ZRTP context hosting the channel * @param[in] selfSSRC The SSRC identifying the channel * @param[in] timeReference The current time in ms * * @return 0 on succes, error code otherwise */ int bzrtp_iterate(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint64_t timeReference) { /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* update the context time reference used when arming timers */ zrtpContext->timeReference = timeReference; if (zrtpChannelContext->timer.status == BZRTP_TIMER_ON) { if (zrtpChannelContext->timer.firingTime<=timeReference) { /* we must trig the timer */ bzrtpEvent_t timerEvent; zrtpChannelContext->timer.firingCount++; /* create a timer event */ timerEvent.eventType = BZRTP_EVENT_TIMER; timerEvent.bzrtpPacketString = NULL; timerEvent.bzrtpPacketStringLength = 0; timerEvent.bzrtpPacket = NULL; timerEvent.zrtpContext = zrtpContext; timerEvent.zrtpChannelContext = zrtpChannelContext; /* send it to the state machine*/ if (zrtpChannelContext->stateMachine != NULL) { return zrtpChannelContext->stateMachine(timerEvent); } } } return 0; } /* * @brief Set the client data pointer in a channel context * This pointer is returned to the client by the callbacks function, used to store associated contexts (RTP session) * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel to be linked to the client Data * @param[in] clientData The clientData pointer, casted to a (void *) * * @return 0 on success * */ int bzrtp_setClientData(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, void *clientData) { /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } zrtpChannelContext->clientData = clientData; return 0; } /* * @brief Process a received message * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel receiving the message * @param[in] zrtpPacketString The packet received * @param[in] zrtpPacketStringLength Length of the packet in bytes * * @return 0 on success, errorcode otherwise */ int bzrtp_processMessage(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *zrtpPacketString, uint16_t zrtpPacketStringLength) { int retval; bzrtpPacket_t *zrtpPacket; bzrtpEvent_t event; /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* check the context is initialised (we may receive packets before initialisation is complete i.e. between channel initialisation and channel start) */ if (zrtpChannelContext->stateMachine == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; /* drop the message */ } /* first check the packet */ zrtpPacket = bzrtp_packetCheck(zrtpPacketString, zrtpPacketStringLength, zrtpChannelContext->peerSequenceNumber, &retval); if (retval != 0) { /*TODO: check the returned error code and do something or silent drop? */ return retval; } /* TODO: Intercept error and ping zrtp packets */ /* if we have a ping packet, just answer with a ping ACK and do not forward to the state machine */ if (zrtpPacket->messageType == MSGTYPE_PING) { bzrtpPacket_t *pingAckPacket = NULL; bzrtp_packetParser(zrtpContext, zrtpChannelContext, zrtpPacketString, zrtpPacketStringLength, zrtpPacket); /* store ping packet in the channel context as packet creator will need it to create the pingACK */ zrtpChannelContext->pingPacket = zrtpPacket; /* create the pingAck packet */ pingAckPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_PINGACK, &retval); if (retval == 0) { retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, pingAckPacket, zrtpChannelContext->selfSequenceNumber); if (retval==0 && zrtpContext->zrtpCallbacks.bzrtp_sendData!=NULL) { /* send the packet */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, pingAckPacket->packetString, pingAckPacket->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } } /* free packets and reset channel context storage */ bzrtp_freeZrtpPacket(zrtpPacket); bzrtp_freeZrtpPacket(pingAckPacket); zrtpChannelContext->pingPacket = NULL; return retval; } /* build a packet event of it and send it to the state machine */ event.eventType = BZRTP_EVENT_MESSAGE; event.bzrtpPacketString = zrtpPacketString; event.bzrtpPacketStringLength = zrtpPacketStringLength; event.bzrtpPacket = zrtpPacket; event.zrtpContext = zrtpContext; event.zrtpChannelContext = zrtpChannelContext; return zrtpChannelContext->stateMachine(event); } /* * @brief Called by user when the SAS has been verified * update the cache(if any) to set the previously verified flag * * @param[in/out] zrtpContext The ZRTP context we're dealing with */ void bzrtp_SASVerified(bzrtpContext_t *zrtpContext) { if (zrtpContext != NULL) { uint8_t pvsFlag = 1; const char *colNames[] = {"pvs"}; uint8_t *colValues[] = {&pvsFlag}; size_t colLength[] = {1}; /* check if we must update the cache(delayed until sas verified in case of cache mismatch) */ if (zrtpContext->cacheMismatchFlag == 1) { zrtpContext->cacheMismatchFlag = 0; bzrtp_updateCachedSecrets(zrtpContext, zrtpContext->channelContext[0]); /* channel[0] is the only one in DHM mode, so the only one able to have a cache mismatch */ } bzrtp_cache_write_lock(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 1, zrtpContext->zidCacheMutex); } } /* * @brief Called by user when the SAS has been set to unverified * update the cache(if any) to unset the previously verified flag * * @param[in/out] zrtpContext The ZRTP context we're dealing with */ void bzrtp_resetSASVerified(bzrtpContext_t *zrtpContext) { if (zrtpContext != NULL) { uint8_t pvsFlag = 0; const char *colNames[] = {"pvs"}; uint8_t *colValues[] = {&pvsFlag}; size_t colLength[] = {1}; bzrtp_cache_write_lock(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 1, zrtpContext->zidCacheMutex); } } /* * @brief Allow client to compute an exported according to RFC section 4.5.2 * Check the context is ready(we already have a master exported key and KDF context) * and run KDF(master exported key, "Label", KDF_Context, negotiated hash Length) * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] label Label used in the KDF * @param[in] labelLength Length of previous buffer * @param[out] derivedKey Buffer to store the derived key * @param[in/out] derivedKeyLength Length of previous buffer(updated to fit actual length of data produced if too long) * * @return 0 on succes, error code otherwise */ int bzrtp_exportKey(bzrtpContext_t *zrtpContext, char *label, size_t labelLength, uint8_t *derivedKey, size_t *derivedKeyLength) { /* check we have s0 or exportedKey and KDFContext in channel[0] - export keys is available only on channel 0 completion - see RFC 4.5.2 */ bzrtpChannelContext_t *zrtpChannelContext = zrtpContext->channelContext[0]; if (zrtpContext->peerBzrtpVersion == 0x010000) { /* keep compatibility with older implementation of bzrtp */ #ifdef SUPPORT_EXPORTEDKEY_V010000 /* before version 1.1.0 (turned into an int MMmmpp -> 010100) exported keys wrongly derives from given label and s0 direclty instead of deriving one Exported Key from S0 and then as many as needed from the exported key as specified in the RFC section 4.5.2 */ if (zrtpChannelContext->s0 == NULL || zrtpChannelContext->KDFContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* We derive a maximum of hashLength bytes */ if (*derivedKeyLength > zrtpChannelContext->hashLength) { *derivedKeyLength = zrtpChannelContext->hashLength; } bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)label, labelLength, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, *derivedKeyLength, zrtpChannelContext->hmacFunction, derivedKey); #else /* SUPPORT_EXPORTEDKEY_V010000 */ /* We do not support anymore backward compatibility, just do nothing but send an error message*/ if (zrtpContext->zrtpCallbacks.bzrtp_statusMessage!=NULL && zrtpContext->zrtpCallbacks.bzrtp_messageLevel>=BZRTP_MESSAGE_ERROR) { /* use error level as we explicitely compile with no support for older version */ zrtpContext->zrtpCallbacks.bzrtp_statusMessage(zrtpChannelContext->clientData, BZRTP_MESSAGE_ERROR, BZRTP_MESSAGE_PEERVERSIONOBSOLETE, "obsolete bzrtp version are not supported anymore"); } #endif /* SUPPORT_EXPORTEDKEY_V010000 */ } else { /* peer either use version 1.1 of BZRTP or another library, just stick to the RFC to create the export key */ if ((zrtpChannelContext->s0 == NULL && zrtpContext->exportedKey) || zrtpChannelContext->KDFContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* if we didn't already computed the master exported key, do it now */ if (zrtpContext->exportedKey == NULL) { zrtpContext->exportedKeyLength = zrtpChannelContext->hashLength; zrtpContext->exportedKey = (uint8_t *)malloc(zrtpContext->exportedKeyLength*sizeof(uint8_t)); bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Exported key", 12, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpContext->exportedKeyLength, zrtpChannelContext->hmacFunction, zrtpContext->exportedKey); } /* We derive a maximum of hashLength bytes */ if (*derivedKeyLength > zrtpChannelContext->hashLength) { *derivedKeyLength = zrtpChannelContext->hashLength; } bzrtp_keyDerivationFunction(zrtpContext->exportedKey, zrtpChannelContext->hashLength, (uint8_t *)label, labelLength, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, *derivedKeyLength, zrtpChannelContext->hmacFunction, derivedKey); } return 0; } /* * @brief Reset the retransmission timer of a given channel. * Packets will be sent again if appropriate: * - when in responder role, zrtp engine only answer to packets sent by the initiator. * - if we are still in discovery phase, Hello or Commit packets will be resent. * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel to reset * * return 0 on success, error code otherwise */ int bzrtp_resetRetransmissionTimer(bzrtpContext_t *zrtpContext, uint32_t selfSSRC) { /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* reset timer only when not in secure mode yet and for initiator(engine start as initiator so if we call this function in discovery phase, it will reset the timer */ if ((zrtpChannelContext->isSecure == 0) && (zrtpChannelContext->role == BZRTP_ROLE_INITIATOR)) { zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = 0; /* be sure it will trigger at next call to bzrtp_iterate*/ zrtpChannelContext->timer.firingCount = -1; /* -1 to count the initial packet and then retransmit the regular number of packets */ /* reset timerStep to the base value */ if ((zrtpChannelContext->timer.timerStep % NON_HELLO_BASE_RETRANSMISSION_STEP) == 0) { zrtpChannelContext->timer.timerStep = NON_HELLO_BASE_RETRANSMISSION_STEP; } else { zrtpChannelContext->timer.timerStep = HELLO_BASE_RETRANSMISSION_STEP; } } return 0; } /** * @brief Get the supported crypto types * * @param[int] zrtpContext The ZRTP context we're dealing with * @param[in] algoType mapped to defines, must be in [ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE] * @param[out] supportedTypes mapped to uint8_t value of the 4 char strings giving the supported types as string according to rfc section 5.1.2 to 5.1.6 * * @return number of supported types, 0 on error */ uint8_t bzrtp_getSupportedCryptoTypes(bzrtpContext_t *zrtpContext, uint8_t algoType, uint8_t supportedTypes[7]) { if (zrtpContext==NULL) { return 0; } switch(algoType) { case ZRTP_HASH_TYPE: return copyCryptoTypes(supportedTypes, zrtpContext->supportedHash, zrtpContext->hc); case ZRTP_CIPHERBLOCK_TYPE: return copyCryptoTypes(supportedTypes, zrtpContext->supportedCipher, zrtpContext->cc); case ZRTP_AUTHTAG_TYPE: return copyCryptoTypes(supportedTypes, zrtpContext->supportedAuthTag, zrtpContext->ac); case ZRTP_KEYAGREEMENT_TYPE: return copyCryptoTypes(supportedTypes, zrtpContext->supportedKeyAgreement, zrtpContext->kc); case ZRTP_SAS_TYPE: return copyCryptoTypes(supportedTypes, zrtpContext->supportedSas, zrtpContext->sc); default: return 0; } } /** * @brief set the supported crypto types * * @param[int/out] zrtpContext The ZRTP context we're dealing with * @param[in] algoType mapped to defines, must be in [ZRTP_HASH_TYPE, ZRTP_CIPHERBLOCK_TYPE, ZRTP_AUTHTAG_TYPE, ZRTP_KEYAGREEMENT_TYPE or ZRTP_SAS_TYPE] * @param[in] supportedTypes mapped to uint8_t value of the 4 char strings giving the supported types as string according to rfc section 5.1.2 to 5.1.6 * @param[in] supportedTypesCount number of supported crypto types */ void bzrtp_setSupportedCryptoTypes(bzrtpContext_t *zrtpContext, uint8_t algoType, uint8_t supportedTypes[7], uint8_t supportedTypesCount) { uint8_t implementedTypes[7]; uint8_t implementedTypesCount; if (zrtpContext==NULL) { return; } implementedTypesCount = bzrtpUtils_getAvailableCryptoTypes(algoType, implementedTypes); switch(algoType) { case ZRTP_HASH_TYPE: zrtpContext->hc = selectCommonAlgo(supportedTypes, supportedTypesCount, implementedTypes, implementedTypesCount, zrtpContext->supportedHash); bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, zrtpContext->supportedHash, &zrtpContext->hc); break; case ZRTP_CIPHERBLOCK_TYPE: zrtpContext->cc = selectCommonAlgo(supportedTypes, supportedTypesCount, implementedTypes, implementedTypesCount, zrtpContext->supportedCipher); bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, zrtpContext->supportedCipher, &zrtpContext->cc); break; case ZRTP_AUTHTAG_TYPE: zrtpContext->ac = selectCommonAlgo(supportedTypes, supportedTypesCount, implementedTypes, implementedTypesCount, zrtpContext->supportedAuthTag); bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, zrtpContext->supportedAuthTag, &zrtpContext->ac); break; case ZRTP_KEYAGREEMENT_TYPE: zrtpContext->kc = selectCommonAlgo(supportedTypes, supportedTypesCount, implementedTypes, implementedTypesCount, zrtpContext->supportedKeyAgreement); bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, zrtpContext->supportedKeyAgreement, &zrtpContext->kc); break; case ZRTP_SAS_TYPE: zrtpContext->sc = selectCommonAlgo(supportedTypes, supportedTypesCount, implementedTypes, implementedTypesCount, zrtpContext->supportedSas); bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, zrtpContext->supportedSas, &zrtpContext->sc); break; } } /** * @brief Set the peer hello hash given by signaling to a ZRTP channel * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * @param[out] peerHelloHashHexString A NULL terminated string containing the hexadecimal form of the hash received in signaling, * may contain ZRTP version as header. * @param[in] peerHelloHashHexStringLength Length of hash string (shall be at least 64 as the hash is a SHA256 so 32 bytes, * more if it contains the version header) * * @return 0 on success, errorcode otherwise */ int bzrtp_setPeerHelloHash(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *peerHelloHashHexString, size_t peerHelloHashHexStringLength) { size_t i=0; uint8_t *hexHashString = NULL; size_t hexHashStringLength = peerHelloHashHexStringLength; /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* parse the given peerHelloHash, it may formatted or just */ /* just ignore anything(we do not care about version number) before a ' ' if found */ while (hexHashString==NULL && ipeerHelloHash) { free(zrtpChannelContext->peerHelloHash); } zrtpChannelContext->peerHelloHash = (uint8_t *)malloc(hexHashStringLength/2*sizeof(uint8_t)); /* convert to uint8 the hex string */ bzrtp_strToUint8(zrtpChannelContext->peerHelloHash, hexHashString, hexHashStringLength); /* Do we already have the peer Hello packet, if yes, check it match the hash */ if (zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID] != NULL) { uint8_t computedPeerHelloHash[32]; /* compute hash using implicit hash function: SHA256, skip packet header in the packetString buffer as the hash must be computed on message only */ bctbx_sha256(zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength, 32, computedPeerHelloHash); /* check they are the same */ if (memcmp(computedPeerHelloHash, zrtpChannelContext->peerHelloHash, 32)!=0) { /* a session already started on this channel but with a wrong Hello we must reset and restart it */ /* note: caller may decide to abort the ZRTP session */ /* reset state Machine */ zrtpChannelContext->stateMachine = NULL; /* set timer off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* destroy and free the key buffers */ bzrtp_DestroyKey(zrtpChannelContext->s0, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->mackeyi, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->mackeyr, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->zrtpkeyi, zrtpChannelContext->cipherKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->zrtpkeyr, zrtpChannelContext->cipherKeyLength, zrtpContext->RNGContext); free(zrtpChannelContext->s0); free(zrtpChannelContext->KDFContext); free(zrtpChannelContext->mackeyi); free(zrtpChannelContext->mackeyr); free(zrtpChannelContext->zrtpkeyi); free(zrtpChannelContext->zrtpkeyr); zrtpChannelContext->s0=NULL; zrtpChannelContext->KDFContext=NULL; zrtpChannelContext->mackeyi=NULL; zrtpChannelContext->mackeyr=NULL; zrtpChannelContext->zrtpkeyi=NULL; zrtpChannelContext->zrtpkeyr=NULL; /* free the allocated buffers but not our self Hello packet */ for (i=0; iselfPackets[i]); zrtpChannelContext->selfPackets[i] = NULL; } bzrtp_freeZrtpPacket(zrtpChannelContext->peerPackets[i]); zrtpChannelContext->peerPackets[i] = NULL; } /* destroy and free the srtp and sas struture */ bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.selfSrtpKey, zrtpChannelContext->srtpSecrets.selfSrtpKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.selfSrtpSalt, zrtpChannelContext->srtpSecrets.selfSrtpSaltLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.peerSrtpKey, zrtpChannelContext->srtpSecrets.peerSrtpKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.peerSrtpSalt, zrtpChannelContext->srtpSecrets.peerSrtpSaltLength, zrtpContext->RNGContext); bzrtp_DestroyKey((uint8_t *)zrtpChannelContext->srtpSecrets.sas, zrtpChannelContext->srtpSecrets.sasLength, zrtpContext->RNGContext); free(zrtpChannelContext->srtpSecrets.selfSrtpKey); free(zrtpChannelContext->srtpSecrets.selfSrtpSalt); free(zrtpChannelContext->srtpSecrets.peerSrtpKey); free(zrtpChannelContext->srtpSecrets.peerSrtpSalt); free(zrtpChannelContext->srtpSecrets.sas); /* re-initialise srtpSecrets structure */ zrtpChannelContext->srtpSecrets.selfSrtpKey = NULL; zrtpChannelContext->srtpSecrets.selfSrtpSalt = NULL; zrtpChannelContext->srtpSecrets.peerSrtpKey = NULL; zrtpChannelContext->srtpSecrets.peerSrtpSalt = NULL; zrtpChannelContext->srtpSecrets.selfSrtpKeyLength = 0; zrtpChannelContext->srtpSecrets.selfSrtpSaltLength = 0; zrtpChannelContext->srtpSecrets.peerSrtpKeyLength = 0; zrtpChannelContext->srtpSecrets.peerSrtpSaltLength = 0; zrtpChannelContext->srtpSecrets.cipherAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.cipherKeyLength = 0; zrtpChannelContext->srtpSecrets.authTagAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.sas = NULL; zrtpChannelContext->srtpSecrets.sasLength = 0; zrtpChannelContext->srtpSecrets.hashAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.keyAgreementAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.sasAlgo = ZRTP_UNSET_ALGO; /* reset choosen algo and their functions */ zrtpChannelContext->hashAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->cipherAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->authTagAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->keyAgreementAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->sasAlgo = ZRTP_UNSET_ALGO; bzrtp_updateCryptoFunctionPointers(zrtpChannelContext); /* restart channel */ bzrtp_startChannelEngine(zrtpContext, selfSSRC); return BZRTP_ERROR_HELLOHASH_MISMATCH; } } return 0; } /** * @brief Get the self hello hash from ZRTP channel * * @param[in/out] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * @param[out] output A NULL terminated string containing the hexadecimal form of the hash received in signaling, * contain ZRTP version as header. Buffer must be allocated by caller. * @param[in] outputLength Length of output buffer, shall be at least 70 : 5 chars for version, 64 for the hash itself, SHA256), NULL termination * * @return 0 on success, errorcode otherwise */ int bzrtp_getSelfHelloHash(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t *output, size_t outputLength) { uint8_t helloHash[32]; /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /* check we have the Hello packet in context */ if (zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID] == NULL) { return BZRTP_ERROR_CONTEXTNOTREADY; } /* check output length : version length +' '+64 hex hash + null termination */ if (outputLength < strlen(ZRTP_VERSION)+1+64+1) { return BZRTP_ERROR_OUTPUTBUFFER_LENGTH; } /* compute hash using implicit hash function: SHA256, skip packet header in the packetString buffer as the hash must be computed on message only */ bctbx_sha256(zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength, 32, helloHash); /* add version header */ strcpy((char *)output, ZRTP_VERSION); output[strlen(ZRTP_VERSION)]=' '; /* convert hash to hex string and set it in the output buffer */ bzrtp_int8ToStr(output+strlen(ZRTP_VERSION)+1, helloHash, 32); /* add NULL termination */ output[strlen(ZRTP_VERSION)+1+64]='\0'; return 0; } /** * @brief Set Auxiliary Secret for this channel(shall be used only on primary audio channel) * The given auxSecret is appended to any aux secret found in ZIDcache * This function must be called before reception of peerHello packet * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] auxSecret A buffer holding the auxiliary shared secret to use (see RFC 6189 section 4.3) * @param[in] auxSecretLength lenght of the previous buffer * * @return 0 on success, error code otherwise * * @note The auxiliary shared secret mechanic is used by LIMEv2 for encryption security purposes but might be used for its original purpose in a regular * ZRTP session if it becomes necessary in the future, or by another encryption engine for example. In that case the API will need an adaptation work. */ int bzrtp_setAuxiliarySharedSecret(bzrtpContext_t *zrtpContext, const uint8_t *auxSecret, size_t auxSecretLength) { if (zrtpContext == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } if (zrtpContext->channelContext[0] && zrtpContext->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] != NULL) { return BZRTP_ERROR_CONTEXTNOTREADY; } /* allocate memory to store the secret - check it wasn't already allocated */ if (zrtpContext->transientAuxSecret) { free(zrtpContext->transientAuxSecret); } zrtpContext->transientAuxSecret = (uint8_t *)malloc(auxSecretLength*sizeof(uint8_t)); /* copy the aux secret and length */ memcpy(zrtpContext->transientAuxSecret, auxSecret, auxSecretLength); zrtpContext->transientAuxSecretLength = auxSecretLength; return 0; } /** * @brief Get the ZRTP auxiliary shared secret mismatch status * * @param[in] zrtpContext The ZRTP context we're dealing with * @return BZRTP_AUXSECRET_MATCH on match, BZRTP_AUXSECRET_MISMATCH on mismatch, BZRTP_AUXSECRET_UNSET if auxiliary shared secret is unused */ uint8_t bzrtp_getAuxiliarySharedSecretMismatch(bzrtpContext_t *zrtpContext) { return zrtpContext->channelContext[0]->srtpSecrets.auxSecretMismatch; } /** * @brief Get the channel status * * @param[in] zrtpContext The ZRTP context we're dealing with * @param[in] selfSSRC The SSRC identifying the channel * * @return BZRTP_CHANNEL_NOTFOUND no channel matching this SSRC doesn't exists in the zrtp context * BZRTP_CHANNEL_INITIALISED channel initialised but not started * BZRTP_CHANNEL_ONGOING ZRTP key exchange in ongoing * BZRTP_CHANNEL_SECURE Channel is secure * BZRTP_CHANNEL_ERROR An error occured on this channel */ int bzrtp_getChannelStatus(bzrtpContext_t *zrtpContext, uint32_t selfSSRC) { /* get channel context */ bzrtpChannelContext_t *zrtpChannelContext = getChannelContext(zrtpContext, selfSSRC); if (zrtpChannelContext == NULL) { return BZRTP_CHANNEL_NOTFOUND; } if (zrtpChannelContext->stateMachine == NULL) { return BZRTP_CHANNEL_INITIALISED; } if (zrtpChannelContext->isSecure == 1) { return BZRTP_CHANNEL_SECURE; } return BZRTP_CHANNEL_ONGOING; } /* Local functions implementation */ /** * @brief Look in the given ZRTP context for a channel referenced with given SSRC * * @param[in] zrtpContext The zrtp context which shall contain the channel context we are looking for * @param[in] selfSSRC The SSRC identifying the channel context * * @return a pointer to the channel context, NULL if the context is invalid or channel not found */ static bzrtpChannelContext_t *getChannelContext(bzrtpContext_t *zrtpContext, uint32_t selfSSRC) { int i; if (zrtpContext==NULL) { return NULL; } for (i=0; ichannelContext[i]!=NULL) { if (zrtpContext->channelContext[i]->selfSSRC == selfSSRC) { return zrtpContext->channelContext[i]; } } } return NULL; /* found no channel with this SSRC */ } /** * @brief Initialise the context of a channel and create and store the Hello packet * Initialise some vectors * * @param[in] zrtpContext The zrtpContext hosting this channel, needed to acces the RNG * @param[out] zrtpChanneContext The channel context to be initialised * @param[in] selfSSRC The SSRC allocated to this channel * @param[in] isMain This channel is channel 0 or an additional channel * * @return 0 on success, error code otherwise */ static int bzrtp_initChannelContext(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, uint32_t selfSSRC, uint8_t isMain) { int i; int retval; bzrtpPacket_t *helloPacket; if (zrtpChannelContext == NULL) { return BZRTP_ERROR_INVALIDCHANNELCONTEXT; } zrtpChannelContext->clientData = NULL; /* the state machine is not started at the creation of the channel but on explicit call to the start function */ zrtpChannelContext->stateMachine = NULL; /* timer is off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; zrtpChannelContext->timer.timerStep = HELLO_BASE_RETRANSMISSION_STEP; /* we must initialise the timeStep just in case the resettimer function is called between init and start */ zrtpChannelContext->selfSSRC = selfSSRC; /* flags */ zrtpChannelContext->isSecure = 0; zrtpChannelContext->isMainChannel = isMain; /* initialise as initiator, switch to responder later if needed */ zrtpChannelContext->role = BZRTP_ROLE_INITIATOR; /* create H0 (32 bytes random) and derive using implicit Hash(SHA256) H1,H2,H3 */ bctbx_rng_get(zrtpContext->RNGContext, zrtpChannelContext->selfH[0], 32); bctbx_sha256(zrtpChannelContext->selfH[0], 32, 32, zrtpChannelContext->selfH[1]); bctbx_sha256(zrtpChannelContext->selfH[1], 32, 32, zrtpChannelContext->selfH[2]); bctbx_sha256(zrtpChannelContext->selfH[2], 32, 32, zrtpChannelContext->selfH[3]); /* initialisation of packet storage */ for (i=0; iselfPackets[i] = NULL; zrtpChannelContext->peerPackets[i] = NULL; } zrtpChannelContext->peerHelloHash = NULL; /* initialise the self Sequence number to a random and peer to 0 */ bctbx_rng_get(zrtpContext->RNGContext, (uint8_t *)&(zrtpChannelContext->selfSequenceNumber), 2); zrtpChannelContext->selfSequenceNumber &= 0x0FFF; /* first 4 bits to zero in order to avoid reaching FFFF and turning back to 0 */ zrtpChannelContext->selfSequenceNumber++; /* be sure it is not initialised to 0 */ zrtpChannelContext->peerSequenceNumber = 0; /* reset choosen algo and their functions */ zrtpChannelContext->hashAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->cipherAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->authTagAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->keyAgreementAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->sasAlgo = ZRTP_UNSET_ALGO; bzrtp_updateCryptoFunctionPointers(zrtpChannelContext); /* initialise key buffers */ zrtpChannelContext->s0 = NULL; zrtpChannelContext->KDFContext = NULL; zrtpChannelContext->KDFContextLength = 0; zrtpChannelContext->mackeyi = NULL; zrtpChannelContext->mackeyr = NULL; zrtpChannelContext->zrtpkeyi = NULL; zrtpChannelContext->zrtpkeyr = NULL; /* initialise srtpSecrets structure */ zrtpChannelContext->srtpSecrets.selfSrtpKey = NULL; zrtpChannelContext->srtpSecrets.selfSrtpSalt = NULL; zrtpChannelContext->srtpSecrets.peerSrtpKey = NULL; zrtpChannelContext->srtpSecrets.peerSrtpSalt = NULL; zrtpChannelContext->srtpSecrets.selfSrtpKeyLength = 0; zrtpChannelContext->srtpSecrets.selfSrtpSaltLength = 0; zrtpChannelContext->srtpSecrets.peerSrtpKeyLength = 0; zrtpChannelContext->srtpSecrets.peerSrtpSaltLength = 0; zrtpChannelContext->srtpSecrets.cipherAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.cipherKeyLength = 0; zrtpChannelContext->srtpSecrets.authTagAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.sas = NULL; zrtpChannelContext->srtpSecrets.sasLength = 0; zrtpChannelContext->srtpSecrets.hashAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.keyAgreementAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.sasAlgo = ZRTP_UNSET_ALGO; zrtpChannelContext->srtpSecrets.cacheMismatch = 0; zrtpChannelContext->srtpSecrets.auxSecretMismatch = BZRTP_AUXSECRET_UNSET; /* create the Hello packet and store it */ helloPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_HELLO, &retval); if (retval != 0) { return retval; } /* build the packet string and store the packet */ if (bzrtp_packetBuild(zrtpContext, zrtpChannelContext, helloPacket, zrtpChannelContext->selfSequenceNumber) ==0) { zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID] = helloPacket; } else { bzrtp_freeZrtpPacket(helloPacket); return retval; } return 0; } /** * @brief Destroy the context of a channel * Free allocated buffers, destroy keys * * @param[in] zrtpContext The zrtpContext hosting this channel, needed to acces the RNG * @param[in] zrtpChannelContext The channel context to be destroyed */ static void bzrtp_destroyChannelContext(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { int i; /* check there is something to be freed */ if (zrtpChannelContext == NULL) { return; } /* reset state Machine */ zrtpChannelContext->stateMachine = NULL; /* set timer off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* destroy and free the key buffers */ bzrtp_DestroyKey(zrtpChannelContext->s0, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->mackeyi, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->mackeyr, zrtpChannelContext->hashLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->zrtpkeyi, zrtpChannelContext->cipherKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->zrtpkeyr, zrtpChannelContext->cipherKeyLength, zrtpContext->RNGContext); free(zrtpChannelContext->s0); free(zrtpChannelContext->KDFContext); free(zrtpChannelContext->mackeyi); free(zrtpChannelContext->mackeyr); free(zrtpChannelContext->zrtpkeyi); free(zrtpChannelContext->zrtpkeyr); zrtpChannelContext->s0=NULL; zrtpChannelContext->KDFContext=NULL; zrtpChannelContext->mackeyi=NULL; zrtpChannelContext->mackeyr=NULL; zrtpChannelContext->zrtpkeyi=NULL; zrtpChannelContext->zrtpkeyr=NULL; /* free the allocated buffers */ for (i=0; iselfPackets[i]); bzrtp_freeZrtpPacket(zrtpChannelContext->peerPackets[i]); zrtpChannelContext->selfPackets[i] = NULL; zrtpChannelContext->peerPackets[i] = NULL; } free(zrtpChannelContext->peerHelloHash); zrtpChannelContext->peerHelloHash = NULL; /* destroy and free the srtp and sas struture */ bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.selfSrtpKey, zrtpChannelContext->srtpSecrets.selfSrtpKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.selfSrtpSalt, zrtpChannelContext->srtpSecrets.selfSrtpSaltLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.peerSrtpKey, zrtpChannelContext->srtpSecrets.peerSrtpKeyLength, zrtpContext->RNGContext); bzrtp_DestroyKey(zrtpChannelContext->srtpSecrets.peerSrtpSalt, zrtpChannelContext->srtpSecrets.peerSrtpSaltLength, zrtpContext->RNGContext); bzrtp_DestroyKey((uint8_t *)zrtpChannelContext->srtpSecrets.sas, zrtpChannelContext->srtpSecrets.sasLength, zrtpContext->RNGContext); free(zrtpChannelContext->srtpSecrets.selfSrtpKey); free(zrtpChannelContext->srtpSecrets.selfSrtpSalt); free(zrtpChannelContext->srtpSecrets.peerSrtpKey); free(zrtpChannelContext->srtpSecrets.peerSrtpSalt); free(zrtpChannelContext->srtpSecrets.sas); /* free the channel context */ free(zrtpChannelContext); } static uint8_t copyCryptoTypes(uint8_t destination[7], uint8_t source[7], uint8_t size) { int i; for (i=0; i. */ #include #include #include #include "cryptoUtils.h" #include "bctoolbox/crypto.h" /** Return available crypto functions. For now we have * * - Hash: HMAC-SHA256(Mandatory) * - CipherBlock: AES128(Mandatory), AES256(optional) * - Auth Tag: HMAC-SHA132 and HMAC-SHA180 (These are mandatory for SRTP and depends on the SRTP implementation thus we can just suppose they are both available) * - Key Agreement: ECDH25519(not mentionned in RFC), ECDH448(not mentionned in RFC), DHM3k(Mandatory), DHM2k(optional and shall not be used except on low power devices) * - Sas: base32(Mandatory), b256(pgp words, optional) */ uint8_t bzrtpUtils_getAvailableCryptoTypes(uint8_t algoType, uint8_t availableTypes[7]) { switch(algoType) { case ZRTP_HASH_TYPE: availableTypes[0] = ZRTP_HASH_S256; availableTypes[1] = ZRTP_HASH_S384; return 2; case ZRTP_CIPHERBLOCK_TYPE: availableTypes[0] = ZRTP_CIPHER_AES1; availableTypes[1] = ZRTP_CIPHER_AES3; return 2; case ZRTP_AUTHTAG_TYPE: availableTypes[0] = ZRTP_AUTHTAG_HS32; availableTypes[1] = ZRTP_AUTHTAG_HS80; return 2; case ZRTP_KEYAGREEMENT_TYPE: { /* get availables types from bctoolbox */ uint32_t available_key_agreements = bctbx_key_agreement_algo_list(); uint8_t index=0; if (available_key_agreements&BCTBX_ECDH_X25519) { availableTypes[index] = ZRTP_KEYAGREEMENT_X255; index++; } if (available_key_agreements&BCTBX_ECDH_X448) { availableTypes[index] = ZRTP_KEYAGREEMENT_X448; index++; } /* DH3k is mandatory*/ availableTypes[index] = ZRTP_KEYAGREEMENT_DH3k; index++; if (available_key_agreements|BCTBX_DHM_2048) { availableTypes[index] = ZRTP_KEYAGREEMENT_DH2k; index++; } availableTypes[index] = ZRTP_KEYAGREEMENT_Mult; /* This one shall always be at the end of the list, it is just to inform the peer ZRTP endpoint that we support the Multichannel ZRTP */ return index+1; } case ZRTP_SAS_TYPE: /* the SAS function is implemented in cryptoUtils.c and then is not directly linked to the polarSSL crypto wrapper */ availableTypes[0] = ZRTP_SAS_B32; availableTypes[1] = ZRTP_SAS_B256; return 2; default: return 0; } } /** Return mandatory crypto functions. For now we have * * - Hash: HMAC-SHA256 * - CipherBlock: AES128 * - Auth Tag: HMAC-SHA132 and HMAC-SHA180 * - Key Agreement: DHM3k * - Sas: base32 */ uint8_t bzrtpUtils_getMandatoryCryptoTypes(uint8_t algoType, uint8_t mandatoryTypes[7]) { switch(algoType) { case ZRTP_HASH_TYPE: mandatoryTypes[0] = ZRTP_HASH_S256; return 1; case ZRTP_CIPHERBLOCK_TYPE: mandatoryTypes[0] = ZRTP_CIPHER_AES1; return 1; case ZRTP_AUTHTAG_TYPE: mandatoryTypes[0] = ZRTP_AUTHTAG_HS32; mandatoryTypes[1] = ZRTP_AUTHTAG_HS80; return 2; case ZRTP_KEYAGREEMENT_TYPE: mandatoryTypes[0] = ZRTP_KEYAGREEMENT_DH3k; mandatoryTypes[1] = ZRTP_KEYAGREEMENT_Mult; /* we must add this one if we want to be able to make multistream */ return 2; case ZRTP_SAS_TYPE: mandatoryTypes[0] = ZRTP_SAS_B32; return 1; default: return 0; } } int bzrtp_keyDerivationFunction(const uint8_t *key, const size_t keyLength, const uint8_t *label, const size_t labelLength, const uint8_t *context, const size_t contextLength, const uint16_t hmacLength, void (*hmacFunction)(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t, uint8_t *), uint8_t *output) { /* get the total length (in bytes) of the data to be hashed */ /* need to add 4 bytes for the initial constant 0x00000001, 1 byte for the 0x00 separator and 4 bytes for the hmacLength length */ uint32_t inputLength = 4 + labelLength + 1 + contextLength + 4; /* create the hmac function input */ uint8_t *input = (uint8_t *)malloc(inputLength*sizeof(uint8_t)); /* fill the input starting by the 32-bits big-endian interger set to 0x00000001 */ uint32_t index = 0; input[index++] = 0x00; input[index++] = 0x00; input[index++] = 0x00; input[index++] = 0x01; /* concat the label */ memcpy(input+index, label, labelLength); index += labelLength; /* a separation byte to 0x00 */ input[index++] = 0x00; /* concat the context string */ memcpy(input+index, context, contextLength); index += contextLength; /* end by L(hmacLength) in big-endian. hamcLength is in bytes and must be converted to bits before insertion in the text to hash */ input[index++] = (uint8_t)((hmacLength>>21)&0xFF); input[index++] = (uint8_t)((hmacLength>>13)&0xFF); input[index++] = (uint8_t)((hmacLength>>5)&0xFF); input[index++] = (uint8_t)((hmacLength<<3)&0xFF); /* call the hmac function */ hmacFunction(key, keyLength, input, inputLength, hmacLength, output); free(input); return 0; } /* Base32 function. Code from rfc section 5.1.6 */ void bzrtp_base32(uint32_t sas, char *output, int outputSize) { int i, n, shift; for (i=0,shift=27; i!=4; ++i,shift-=5) { n = (sas>>shift) & 31; output[i] = "ybndrfg8ejkmcpqxot1uwisza345h769"[n]; } output[4] = '\0'; } /* Base256 function. Code from rfc section 5.1.6 */ void bzrtp_base256(uint32_t sas, char *output, int outputSize) { // generate indexes and copy the appropriate words int evenIndex = (sas >> 24) & 0xFF; int oddIndex = (sas >> 16) & 0xFF; snprintf(output, outputSize, "%s:%s", pgpWordsEven[evenIndex], pgpWordsOdd[oddIndex]); } uint32_t CRC32LookupTable[256] = { 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 }; /* CRC32 Polynomial 0x1EDC6F41 in reverse bit order so * used the reversed one 0x82F63B78 to compute the table */ uint32_t bzrtp_CRC32(uint8_t *input, uint16_t length) { int i; /* code used to generate the lookup table but it's faster to store it */ /* int j; uint32_t CRC32LookupTable[256]; for (i = 0; i <= 0xFF; i++) { uint32_t crcT = i; for (j = 0; j < 8; j++) { crcT = (crcT >> 1) ^ ((crcT & 1) * 0x82F63B78); } CRC32LookupTable[i] = crcT; } for (i=0; i<256; i+=4) { printf("0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx,\n", (long unsigned int)CRC32LookupTable[i], (long unsigned int)CRC32LookupTable[i+1], (long unsigned int)CRC32LookupTable[i+2], (long unsigned int)CRC32LookupTable[i+3]); }*/ uint32_t crc = 0xFFFFFFFF; for (i=0; i> 8) ^ CRC32LookupTable[(crc & 0xFF) ^ input[i]]; } crc =~crc; /* swap the byte order */ return ((crc&0xFF000000)>>24)|((crc&0x00FF0000)>>8)|((crc&0x0000FF00)<<8)|((crc&0x000000FF)<<24); } /* * @brief select a key agreement algorithm from the one available in context and the one provided by * peer in Hello Message as described in rfc section 4.1.2 * - other algorithm are selected according to availability and selected key agreement as described in * rfc section 5.1.5 * The other algorithm choice will finally be set by the endpoint acting as initiator in the commit packet * * @param[in/out] zrtpContext The context contains the list of available algo and is set with the selected ones and associated functions * @param[in] peerHelloMessage The peer hello message containing his set of available algos * * return 0 on succes, error code otherwise * */ int bzrtp_cryptoAlgoAgreement(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpHelloMessage_t *peerHelloMessage) { uint8_t selfCommonKeyAgreementType[7]; uint8_t peerCommonKeyAgreementType[7]; uint8_t commonKeyAgreementTypeNumber = 0; uint8_t commonCipherType[7]; uint8_t commonCipherTypeNumber; uint8_t commonHashType[7]; uint8_t commonHashTypeNumber; uint8_t commonAuthTagType[7]; uint8_t commonAuthTagTypeNumber; uint8_t commonSasType[7]; uint8_t commonSasTypeNumber; /* check context and Message */ if (zrtpContext == NULL) { return ZRTP_CRYPTOAGREEMENT_INVALIDCONTEXT; } if (zrtpContext->kc == 0) { return ZRTP_CRYPTOAGREEMENT_INVALIDCONTEXT; } if (peerHelloMessage == NULL) { return ZRTP_CRYPTOAGREEMENT_INVALIDMESSAGE; } if (peerHelloMessage->kc == 0) { return ZRTP_CRYPTOAGREEMENT_INVALIDMESSAGE; } /* now check what is in common in self and peer order */ /* self ordering: get the common list in the self order of preference */ commonKeyAgreementTypeNumber = selectCommonAlgo(zrtpContext->supportedKeyAgreement, zrtpContext->kc, peerHelloMessage->supportedKeyAgreement, peerHelloMessage->kc, selfCommonKeyAgreementType); /* check we have something in common */ if (commonKeyAgreementTypeNumber == 0) { /* this shall never bee true as all ZRTP endpoint MUST support at least DH3k */ return ZRTP_CRYPTOAGREEMENT_NOCOMMONALGOFOUND; } /* peer ordering: get the common list in the peer order of preference */ selectCommonAlgo(peerHelloMessage->supportedKeyAgreement, peerHelloMessage->kc, zrtpContext->supportedKeyAgreement, zrtpContext->kc, peerCommonKeyAgreementType); /* if the first choices are the same for both, select it */ if (selfCommonKeyAgreementType[0] == peerCommonKeyAgreementType[0]) { zrtpChannelContext->keyAgreementAlgo = selfCommonKeyAgreementType[0]; } else { /* select the fastest of the two algoritm. Order is "DH2k", "E255", "EC25", "E448", "DH3k", "EC38", "EC52" is defined by value order from bzrtp.h */ if (peerCommonKeyAgreementType[0]keyAgreementAlgo = peerCommonKeyAgreementType[0]; } else { zrtpChannelContext->keyAgreementAlgo = selfCommonKeyAgreementType[0]; } } /* now we shall select others algos among the availables and set them into the context, these choices may be * bypassed if we assume the receptor role as the initiator's commit will have the final word on the algo * selection */ /*** Cipher block algorithm ***/ /* get the self cipher types availables */ commonCipherTypeNumber = selectCommonAlgo(zrtpContext->supportedCipher, zrtpContext->cc, peerHelloMessage->supportedCipher, peerHelloMessage->cc, commonCipherType); if (commonCipherTypeNumber == 0) {/* This shall never happend but... */ return ZRTP_CRYPTOAGREEMENT_INVALIDCIPHER; } /* rfc section 5.1.5 specifies that if EC38 is choosen we SHOULD use AES256 or AES192 */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_EC38) { int i=0; zrtpChannelContext->cipherAlgo = ZRTP_UNSET_ALGO; /* is AES3 available */ while (icipherAlgo == ZRTP_UNSET_ALGO) { if (commonCipherType[i] == ZRTP_CIPHER_AES3) { zrtpChannelContext->cipherAlgo = ZRTP_CIPHER_AES3; } i++; } /* is AES2 available */ if (zrtpChannelContext->cipherAlgo == ZRTP_UNSET_ALGO) { i=0; while (icipherAlgo == ZRTP_UNSET_ALGO) { if (commonCipherType[i] == ZRTP_CIPHER_AES2) { zrtpChannelContext->cipherAlgo = ZRTP_CIPHER_AES2; } i++; } } if (zrtpChannelContext->cipherAlgo == ZRTP_UNSET_ALGO) { return ZRTP_CRYPTOAGREEMENT_INVALIDCIPHER; } } else { /* no restrictions, pick the first one */ zrtpChannelContext->cipherAlgo = commonCipherType[0]; } /*** Hash algorithm ***/ /* get the self hash types availables */ commonHashTypeNumber = selectCommonAlgo(zrtpContext->supportedHash, zrtpContext->hc, peerHelloMessage->supportedHash, peerHelloMessage->hc, commonHashType); if (commonHashTypeNumber == 0) {/* This shall never happend but... */ return ZRTP_CRYPTOAGREEMENT_INVALIDHASH; } /* rfc section 5.1.5 specifies that if EC38 is choosen we SHOULD use SHA384 */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_EC38) { int i=0; zrtpChannelContext->hashAlgo = ZRTP_UNSET_ALGO; /* is S384 available */ while (ihashAlgo == ZRTP_UNSET_ALGO) { if (commonHashType[i] == ZRTP_HASH_S384) { zrtpChannelContext->hashAlgo = ZRTP_HASH_S384; } i++; } if (zrtpChannelContext->hashAlgo == ZRTP_UNSET_ALGO) { return ZRTP_CRYPTOAGREEMENT_INVALIDHASH; } } else { /* no restrictions, pick the first one */ zrtpChannelContext->hashAlgo = commonHashType[0]; } /*** Authentication Tag algorithm ***/ /* get the self authentication tag types availables */ commonAuthTagTypeNumber = selectCommonAlgo(zrtpContext->supportedAuthTag, zrtpContext->ac, peerHelloMessage->supportedAuthTag, peerHelloMessage->ac, commonAuthTagType); if (commonAuthTagTypeNumber == 0) {/* This shall never happend but... */ return ZRTP_CRYPTOAGREEMENT_INVALIDAUTHTAG; } zrtpChannelContext->authTagAlgo = commonAuthTagType[0]; /*** Sas algorithm ***/ /* get the self Sas rendering types availables */ commonSasTypeNumber = selectCommonAlgo(zrtpContext->supportedSas, zrtpContext->sc, peerHelloMessage->supportedSas, peerHelloMessage->sc, commonSasType); if (commonSasTypeNumber == 0) {/* This shall never happend but... */ return ZRTP_CRYPTOAGREEMENT_INVALIDSAS; } zrtpChannelContext->sasAlgo = commonSasType[0]; /* update the function pointers */ return bzrtp_updateCryptoFunctionPointers(zrtpChannelContext); } /* * @brief Update context crypto function pointer according to related values of choosen algorithms fields (hashAlgo, cipherAlgo, etc..) * The associated length are updated too * * @param[in/out] context The bzrtp context to be updated * * @return 0 on succes */ int bzrtp_updateCryptoFunctionPointers(bzrtpChannelContext_t *zrtpChannelContext) { if (zrtpChannelContext==NULL) { return ZRTP_CRYPTOAGREEMENT_INVALIDCONTEXT; } /* Hash algo */ switch (zrtpChannelContext->hashAlgo) { case ZRTP_HASH_S256 : zrtpChannelContext->hashFunction = bctbx_sha256; zrtpChannelContext->hmacFunction = bctbx_hmacSha256; zrtpChannelContext->hashLength = 32; break; case ZRTP_HASH_S384 : zrtpChannelContext->hashFunction = bctbx_sha384; zrtpChannelContext->hmacFunction = bctbx_hmacSha384; zrtpChannelContext->hashLength = 48; break; case ZRTP_UNSET_ALGO : zrtpChannelContext->hashFunction = NULL; zrtpChannelContext->hmacFunction = NULL; zrtpChannelContext->hashLength = 0; break; default: return ZRTP_CRYPTOAGREEMENT_INVALIDHASH; break; } /* CipherBlock algo */ switch (zrtpChannelContext->cipherAlgo) { case ZRTP_CIPHER_AES1 : zrtpChannelContext->cipherEncryptionFunction = bctbx_aes128CfbEncrypt; zrtpChannelContext->cipherDecryptionFunction = bctbx_aes128CfbDecrypt; zrtpChannelContext->cipherKeyLength = 16; break; case ZRTP_CIPHER_AES3 : zrtpChannelContext->cipherEncryptionFunction = bctbx_aes256CfbEncrypt; zrtpChannelContext->cipherDecryptionFunction = bctbx_aes256CfbDecrypt; zrtpChannelContext->cipherKeyLength = 32; break; case ZRTP_UNSET_ALGO : zrtpChannelContext->cipherEncryptionFunction = NULL; zrtpChannelContext->cipherDecryptionFunction = NULL; zrtpChannelContext->cipherKeyLength = 0; break; default: return ZRTP_CRYPTOAGREEMENT_INVALIDCIPHER; break; } /* Key agreement algo : there is an unique function for this one in the wrapper, just set the keyAgreementLength */ switch (zrtpChannelContext->keyAgreementAlgo) { case ZRTP_KEYAGREEMENT_DH2k : zrtpChannelContext->keyAgreementLength = 256; break; case ZRTP_KEYAGREEMENT_DH3k : zrtpChannelContext->keyAgreementLength = 384; break; case ZRTP_KEYAGREEMENT_X255 : zrtpChannelContext->keyAgreementLength = 32; break; case ZRTP_KEYAGREEMENT_X448 : zrtpChannelContext->keyAgreementLength = 56; break; default: return ZRTP_CRYPTOAGREEMENT_INVALIDCIPHER; break; } /* SAS rendering algo */ switch(zrtpChannelContext->sasAlgo) { case ZRTP_SAS_B32: zrtpChannelContext->sasFunction = bzrtp_base32; // extend 4 byte b32 length to include null terminator zrtpChannelContext->sasLength = 5; break; case ZRTP_SAS_B256: zrtpChannelContext->sasFunction = bzrtp_base256; zrtpChannelContext->sasLength = 32; break; case ZRTP_UNSET_ALGO : zrtpChannelContext->sasFunction = NULL; zrtpChannelContext->sasLength = 0; break; default: return ZRTP_CRYPTOAGREEMENT_INVALIDSAS; break; } return 0; } #define BITS_PRO_INT 8*sizeof(int) #define BITMASK_256_SIZE 256/BITS_PRO_INT #define BITMASK_256_SET_ZERO(bitmask) memset(bitmask, 0, sizeof(int)*BITMASK_256_SIZE) #define BITMASK_256_SET(bitmask, value) bitmask[value/BITS_PRO_INT] |= 1 << (value % BITS_PRO_INT) #define BITMASK_256_UNSET(bitmask, value) bitmask[value/BITS_PRO_INT] &= ~(1 << (value % BITS_PRO_INT)) #define BITMASK_256_CHECK(bitmask, value) (bitmask[value/BITS_PRO_INT] & 1 << (value % BITS_PRO_INT)) /** * @brief Select common algorithm from the given array where algo are represented by their 4 chars string defined in rfc section 5.1.2 to 5.1.6 * Master array is the one given the preference order * All algo are designed by their uint8_t mapped values * * @param[in] masterArray The ordered available algo, result will follow this ordering * @param[in] masterArrayLength Number of valids element in the master array * @param[in] slaveArray The available algo, order is not taken in account * @param[in] slaveArrayLength Number of valids element in the slave array * @param[out] commonArray Common algorithms found, max size 7 * * @return the number of common algorithms found */ uint8_t selectCommonAlgo(uint8_t masterArray[7], uint8_t masterArrayLength, uint8_t slaveArray[7], uint8_t slaveArrayLength, uint8_t commonArray[7]) { int i; uint8_t commonLength = 0; int algosBitmap[BITMASK_256_SIZE]; BITMASK_256_SET_ZERO(algosBitmap); for (i=0; i0 && i>4)&0x0F); outputString[2*i+1] = bzrtp_byteToChar(inputBytes[i]&0x0F); } } /** * @brief convert an hexa char [0-9a-fA-F] into the corresponding unsigned integer value * Any invalid char will be converted to zero without any warning * * @param[in] inputChar a char which shall be in range [0-9a-fA-F] * * @return the unsigned integer value in range [0-15] */ uint8_t bzrtp_charToByte(uint8_t inputChar) { /* 0-9 */ if (inputChar>0x29 && inputChar<0x3A) { return inputChar - 0x30; } /* a-f */ if (inputChar>0x60 && inputChar<0x67) { return inputChar - 0x57; /* 0x57 = 0x61(a) + 0x0A*/ } /* A-F */ if (inputChar>0x40 && inputChar<0x47) { return inputChar - 0x37; /* 0x37 = 0x41(a) + 0x0A*/ } /* shall never arrive here, string is not Hex*/ return 0; } /** * @brief convert a byte which value is in range [0-15] into an hexa char [0-9a-fA-F] * * @param[in] inputByte an integer which shall be in range [0-15] * * @return the hexa char [0-9a-f] corresponding to the input */ uint8_t bzrtp_byteToChar(uint8_t inputByte) { inputByte &=0x0F; /* restrict the input value to range [0-15] */ /* 0-9 */ if(inputByte<0x0A) { return inputByte+0x30; } /* a-f */ return inputByte + 0x57; } bzrtp-4.4.13/src/packetParser.c000066400000000000000000002160441364144501400163340ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "typedef.h" #include "packetParser.h" #include #include "cryptoUtils.h" /* DEBUG */ #include /* minimum length of a ZRTP packet: 12 bytes header + 12 bytes message(shortest are ACK messages) + 4 bytes CRC */ #define ZRTP_MIN_PACKET_LENGTH 28 /* maximum length of a ZRTP packet: 3072 bytes get it from GNU-ZRTP CPP code */ #define ZRTP_MAX_PACKET_LENGTH 3072 /* header of ZRTP message is 12 bytes : Preambule/Message Length + Message Type(2 words) */ #define ZRTP_MESSAGE_HEADER_LENGTH 12 /* length of the non optional and fixed part of all messages, in bytes */ #define ZRTP_HELLOMESSAGE_FIXED_LENGTH 88 #define ZRTP_HELLOACKMESSAGE_FIXED_LENGTH 12 #define ZRTP_COMMITMESSAGE_FIXED_LENGTH 84 #define ZRTP_DHPARTMESSAGE_FIXED_LENGTH 84 #define ZRTP_CONFIRMMESSAGE_FIXED_LENGTH 76 #define ZRTP_CONF2ACKMESSAGE_FIXED_LENGTH 12 #define ZRTP_ERRORMESSAGE_FIXED_LENGTH 16 #define ZRTP_ERRORACKMESSAGE_FIXED_LENGTH 12 #define ZRTP_GOCLEARMESSAGE_FIXED_LENGTH 20 #define ZRTP_CLEARACKMESSAGE_FIXED_LENGTH 12 #define ZRTP_SASRELAYMESSAGE_FIXED_LENGTH 76 #define ZRTP_RELAYACKMESSAGE_FIXED_LENGTH 12 #define ZRTP_PINGMESSAGE_FIXED_LENGTH 24 #define ZRTP_PINGACKMESSAGE_FIXED_LENGTH 36 /*** local functions prototypes ***/ /** * Return the variable private value length in bytes according to given key agreement algorythm * * @param[in] keyAgreementAlgo The key agreement algo mapped to an integer as defined in cryptoWrapper.h * * @return the private value length in bytes * */ uint16_t computeKeyAgreementPrivateValueLength(uint8_t keyAgreementAlgo); /** * @brief Retrieve the 8 char string value message type from the int32_t code * * @param[in] messageType The messageType code * * @return an 9 char string : 8 chars message type as specified in rfc section 5.1.1 + string terminating char */ uint8_t *messageTypeInttoString(uint32_t messageType); /** * @brief Map the 8 char string value message type to an int32_t * * @param[in] messageTypeString an 8 bytes string matching a zrtp message type * * @return a 32-bits unsigned integer mapping the message type */ int32_t messageTypeStringtoInt(uint8_t messageTypeString[8]); /** * @brief Write the message header(preambule, length, message type) into the given output buffer * * @param[out] outputBuffer Message starts at the begining of this buffer * @param[in] messageLength Message length in bytes! To be converted into 32bits words before being inserted in the message header * @param[in] messageType An 8 chars string for the message type (validity is not checked by this function) * */ void zrtpMessageSetHeader(uint8_t *outputBuffer, uint16_t messageLength, uint8_t messageType[8]); /*** Public functions implementation ***/ /* First call this function to check packet validity and create the packet structure */ bzrtpPacket_t *bzrtp_packetCheck(const uint8_t * input, uint16_t inputLength, uint16_t lastValidSequenceNumber, int *exitCode) { bzrtpPacket_t *zrtpPacket; uint16_t sequenceNumber; uint32_t packetCRC; uint16_t messageLength; uint32_t messageType; /* first check that the packet is a ZRTP one */ /* is the length compatible with a ZRTP packet */ if ((inputLengthZRTP_MAX_PACKET_LENGTH)) { *exitCode = BZRTP_PARSER_ERROR_INVALIDPACKET; return NULL; } /* check ZRTP packet format from rfc section 5 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0 0 0 1|Not Used (set to zero) | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Magic Cookie 'ZRTP' (0x5a525450) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | ZRTP Message (length depends on Message Type) | | . . . | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | CRC (1 word) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ if ((input[0]>>4 != 0x01) || (input[4]!= (uint8_t)((ZRTP_MAGIC_COOKIE>>24)&0xFF)) || (input[5]!= (uint8_t)((ZRTP_MAGIC_COOKIE>>16)&0xFF)) || (input[6]!= (uint8_t)((ZRTP_MAGIC_COOKIE>>8)&0xFF)) || (input[7]!= (uint8_t)(ZRTP_MAGIC_COOKIE&0xFF))) { *exitCode = BZRTP_PARSER_ERROR_INVALIDPACKET; return NULL; } /* Check the sequence number : it must be > to the last valid one (given in parameter) to discard out of order packets * TODO: what if we got a Sequence Number overflowing the 16 bits ? */ sequenceNumber = (((uint16_t)input[2])<<8) | ((uint16_t)input[3]); if (sequenceNumber <= lastValidSequenceNumber) { *exitCode = BZRTP_PARSER_ERROR_OUTOFORDER; return NULL; } /* Check the CRC : The CRC is calculated across the entire ZRTP packet, including the ZRTP header and the ZRTP message, but not including the CRC field.*/ packetCRC = ((((uint32_t)input[inputLength-4])<<24)&0xFF000000) | ((((uint32_t)input[inputLength-3])<<16)&0x00FF0000) | ((((uint32_t)input[inputLength-2])<<8)&0x0000FF00) | (((uint32_t)input[inputLength-1])&0x000000FF); if (bzrtp_CRC32((uint8_t *)input, inputLength - 4) != packetCRC) { *exitCode = BZRTP_PARSER_ERROR_INVALIDCRC; return NULL; } /* check message header : * 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message Type Block (2 words) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ if ((input[ZRTP_PACKET_HEADER_LENGTH]!=0x50) || (input[ZRTP_PACKET_HEADER_LENGTH+1]!=0x5a)) { *exitCode = BZRTP_PARSER_ERROR_INVALIDMESSAGE; return NULL; } /* get the length from the message: it is expressed in 32bits words, convert it to bytes (4*) */ messageLength = 4*(((((uint16_t)input[ZRTP_PACKET_HEADER_LENGTH+2])<<8)&0xFF00) | (((uint16_t)input[ZRTP_PACKET_HEADER_LENGTH+3])&0x00FF)); /* get the message Type */ messageType = messageTypeStringtoInt((uint8_t *)(input+ZRTP_PACKET_HEADER_LENGTH+4)); if (messageType == MSGTYPE_INVALID) { *exitCode = BZRTP_PARSER_ERROR_INVALIDMESSAGE; return NULL; } /* packet and message seems to be valid, so allocate a structure and parse it */ zrtpPacket = (bzrtpPacket_t *)malloc(sizeof(bzrtpPacket_t)); memset(zrtpPacket, 0, sizeof(bzrtpPacket_t)); zrtpPacket->sequenceNumber = sequenceNumber; zrtpPacket->messageLength = messageLength; zrtpPacket->messageType = messageType; zrtpPacket->messageData = NULL; zrtpPacket->packetString = NULL; /* get the SSRC */ zrtpPacket->sourceIdentifier = ((((uint32_t)input[8])<<24)&0xFF000000) | ((((uint32_t)input[9])<<16)&0x00FF0000) | ((((uint32_t)input[10])<<8)&0x0000FF00) | (((uint32_t)input[11])&0x000000FF); *exitCode = 0; return zrtpPacket; } /* Call this function after the packetCheck one, to actually parse the packet : create and fill the messageData structure */ int bzrtp_packetParser(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, const uint8_t * input, uint16_t inputLength, bzrtpPacket_t *zrtpPacket) { int i; /* now allocate and fill the correct message structure according to the message type */ /* messageContent points to the begining of the ZRTP message */ uint8_t *messageContent = (uint8_t *)(input+ZRTP_PACKET_HEADER_LENGTH+ZRTP_MESSAGE_HEADER_LENGTH); switch (zrtpPacket->messageType) { case MSGTYPE_HELLO : { bzrtpHelloMessage_t *messageData; /* Do we have a peerHelloHash to check */ if (zrtpChannelContext->peerHelloHash != NULL) { uint8_t computedPeerHelloHash[32]; /* compute hash using implicit hash function: SHA256, skip packet header in the packetString buffer as the hash must be computed on message only */ bctbx_sha256(input+ZRTP_PACKET_HEADER_LENGTH, inputLength - ZRTP_PACKET_OVERHEAD, 32, computedPeerHelloHash); /* check they are the same */ if (memcmp(computedPeerHelloHash, zrtpChannelContext->peerHelloHash, 32)!=0) { return BZRTP_ERROR_HELLOHASH_MISMATCH; } } /* allocate a Hello message structure */ messageData = (bzrtpHelloMessage_t *)malloc(sizeof(bzrtpHelloMessage_t)); /* fill it */ memcpy(messageData->version, messageContent, 4); messageContent +=4; memcpy(messageData->clientIdentifier, messageContent, 16); messageData->clientIdentifier[16] = '\0'; /* be sure the clientIdentifier is a NULL terminated string */ messageContent +=16; memcpy(messageData->H3, messageContent, 32); messageContent +=32; memcpy(messageData->ZID, messageContent, 12); messageContent +=12; messageData->S = ((*messageContent)>>6)&0x01; messageData->M = ((*messageContent)>>5)&0x01; messageData->P = ((*messageContent)>>4)&0x01; messageContent +=1; messageData->hc = MIN((*messageContent)&0x0F, 7); messageContent +=1; messageData->cc = MIN(((*messageContent)>>4)&0x0F, 7); messageData->ac = MIN((*messageContent)&0x0F, 7); messageContent +=1; messageData->kc = MIN(((*messageContent)>>4)&0x0F, 7); messageData->sc = MIN((*messageContent)&0x0F, 7); messageContent +=1; /* Check message length according to value in hc, cc, ac, kc and sc */ if (zrtpPacket->messageLength != ZRTP_HELLOMESSAGE_FIXED_LENGTH + 4*((uint16_t)(messageData->hc)+(uint16_t)(messageData->cc)+(uint16_t)(messageData->ac)+(uint16_t)(messageData->kc)+(uint16_t)(messageData->sc))) { free(messageData); return BZRTP_PARSER_ERROR_INVALIDMESSAGE; } /* parse the variable length part: algorithms types */ for (i=0; ihc; i++) { messageData->supportedHash[i] = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_HASH_TYPE); messageContent +=4; } for (i=0; icc; i++) { messageData->supportedCipher[i] = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_CIPHERBLOCK_TYPE); messageContent +=4; } for (i=0; iac; i++) { messageData->supportedAuthTag[i] = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_AUTHTAG_TYPE); messageContent +=4; } for (i=0; ikc; i++) { messageData->supportedKeyAgreement[i] = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_KEYAGREEMENT_TYPE); messageContent +=4; } for (i=0; isc; i++) { messageData->supportedSas[i] = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_SAS_TYPE); messageContent +=4; } bzrtp_addMandatoryCryptoTypesIfNeeded(ZRTP_HASH_TYPE, messageData->supportedHash, &messageData->hc); bzrtp_addMandatoryCryptoTypesIfNeeded(ZRTP_CIPHERBLOCK_TYPE, messageData->supportedCipher, &messageData->cc); bzrtp_addMandatoryCryptoTypesIfNeeded(ZRTP_AUTHTAG_TYPE, messageData->supportedAuthTag, &messageData->ac); bzrtp_addMandatoryCryptoTypesIfNeeded(ZRTP_KEYAGREEMENT_TYPE, messageData->supportedKeyAgreement, &messageData->kc); bzrtp_addMandatoryCryptoTypesIfNeeded(ZRTP_SAS_TYPE, messageData->supportedSas, &messageData->sc); memcpy(messageData->MAC, messageContent, 8); /* attach the message structure to the packet one */ zrtpPacket->messageData = (void *)messageData; /* the parsed Hello packet must be saved as it may be used to generate commit message or the total_hash */ zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t)); memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */ } break; /* MSGTYPE_HELLO */ case MSGTYPE_HELLOACK : { /* check message length */ if (zrtpPacket->messageLength != ZRTP_HELLOACKMESSAGE_FIXED_LENGTH) { return BZRTP_PARSER_ERROR_INVALIDMESSAGE; } } break; /* MSGTYPE_HELLOACK */ case MSGTYPE_COMMIT: { uint8_t checkH3[32]; uint8_t checkMAC[32]; bzrtpHelloMessage_t *peerHelloMessageData; uint16_t variableLength = 0; /* allocate a commit message structure */ bzrtpCommitMessage_t *messageData; messageData = (bzrtpCommitMessage_t *)malloc(sizeof(bzrtpCommitMessage_t)); /* fill the structure */ memcpy(messageData->H2, messageContent, 32); messageContent +=32; /* We have now H2, check it matches the H3 we had in the hello message H3=SHA256(H2) and that the Hello message MAC is correct */ if (zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no Hello message in this channel, this commit shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerHelloMessageData = (bzrtpHelloMessage_t *)zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData; /* Check H3 = SHA256(H2) */ bctbx_sha256(messageData->H2, 32, 32, checkH3); if (memcmp(checkH3, peerHelloMessageData->H3, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the hello MAC message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(messageData->H2, 32, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerHelloMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } memcpy(messageData->ZID, messageContent, 12); messageContent +=12; messageData->hashAlgo = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_HASH_TYPE); messageContent += 4; messageData->cipherAlgo = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_CIPHERBLOCK_TYPE); messageContent += 4; messageData->authTagAlgo = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_AUTHTAG_TYPE); messageContent += 4; messageData->keyAgreementAlgo = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_KEYAGREEMENT_TYPE); messageContent += 4; /* commit message length depends on the key agreement type choosen (and set in the zrtpContext->keyAgreementAlgo) */ switch(messageData->keyAgreementAlgo) { case ZRTP_KEYAGREEMENT_DH2k : case ZRTP_KEYAGREEMENT_X255 : case ZRTP_KEYAGREEMENT_X448 : case ZRTP_KEYAGREEMENT_EC25 : case ZRTP_KEYAGREEMENT_DH3k : case ZRTP_KEYAGREEMENT_EC38 : case ZRTP_KEYAGREEMENT_EC52 : variableLength = 32; /* hvi is 32 bytes length in DH Commit message format */ break; case ZRTP_KEYAGREEMENT_Prsh : variableLength = 24; /* nonce (16 bytes) and keyID(8 bytes) are 24 bytes length in preshared Commit message format */ break; case ZRTP_KEYAGREEMENT_Mult : variableLength = 16; /* nonce is 24 bytes length in multistream Commit message format */ break; default: free(messageData); return BZRTP_PARSER_ERROR_INVALIDMESSAGE; } if (zrtpPacket->messageLength != ZRTP_COMMITMESSAGE_FIXED_LENGTH + variableLength) { free(messageData); return BZRTP_PARSER_ERROR_INVALIDMESSAGE; } messageData->sasAlgo = bzrtp_cryptoAlgoTypeStringToInt(messageContent, ZRTP_SAS_TYPE); messageContent += 4; /* if it is a multistream or preshared commit, get the 16 bytes nonce */ if ((messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { memcpy(messageData->nonce, messageContent, 16); messageContent +=16; /* and the keyID for preshared commit only */ if (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { memcpy(messageData->keyID, messageContent, 8); messageContent +=8; } } else { /* it's a DH commit message, get the hvi */ memcpy(messageData->hvi, messageContent, 32); messageContent +=32; } /* get the MAC and attach the message data to the packet structure */ memcpy(messageData->MAC, messageContent, 8); zrtpPacket->messageData = (void *)messageData; /* the parsed commit packet must be saved as it is used to generate the total_hash */ zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t)); memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */ } break; /* MSGTYPE_COMMIT */ case MSGTYPE_DHPART1 : case MSGTYPE_DHPART2 : { bzrtpDHPartMessage_t *messageData; /*check message length, depends on the selected key agreement algo set in zrtpContext */ uint16_t pvLength = computeKeyAgreementPrivateValueLength(zrtpChannelContext->keyAgreementAlgo); if (pvLength == 0) { return BZRTP_PARSER_ERROR_INVALIDCONTEXT; } if (zrtpPacket->messageLength != ZRTP_DHPARTMESSAGE_FIXED_LENGTH+pvLength) { return BZRTP_PARSER_ERROR_INVALIDMESSAGE; } /* allocate a DHPart message structure and pv */ messageData = (bzrtpDHPartMessage_t *)malloc(sizeof(bzrtpDHPartMessage_t)); /* fill the structure */ memcpy(messageData->H1, messageContent, 32); messageContent +=32; /* We have now H1, check it matches the H2 we had in the commit message H2=SHA256(H1) and that the Commit message MAC is correct */ if ( zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { /* do it only if we are responder (we received a commit packet) */ uint8_t checkH2[32]; uint8_t checkMAC[32]; bzrtpCommitMessage_t *peerCommitMessageData; if (zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no Commit message in this channel, this DHPart2 shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerCommitMessageData = (bzrtpCommitMessage_t *)zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageData; /* Check H2 = SHA256(H1) */ bctbx_sha256(messageData->H1, 32, 32, checkH2); if (memcmp(checkH2, peerCommitMessageData->H2, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the Commit MAC message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(messageData->H1, 32, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerCommitMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } /* Check the hvi received in the commit message - RFC section 4.4.1.1*/ /* First compute the expected hvi */ /* hvi = hash(initiator's DHPart2 message(current zrtpPacket)) || responder's Hello message) using the agreed hash function truncated to 256 bits */ /* create a string with the messages concatenated */ { uint8_t computedHvi[32]; uint16_t HelloMessageLength = zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength; uint16_t DHPartHelloMessageStringLength = zrtpPacket->messageLength + HelloMessageLength; uint8_t *DHPartHelloMessageString = (uint8_t *)malloc(DHPartHelloMessageStringLength*sizeof(uint8_t)); memcpy(DHPartHelloMessageString, input+ZRTP_PACKET_HEADER_LENGTH, zrtpPacket->messageLength); memcpy(DHPartHelloMessageString+zrtpPacket->messageLength, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, HelloMessageLength); zrtpChannelContext->hashFunction(DHPartHelloMessageString, DHPartHelloMessageStringLength, 32, computedHvi); free(DHPartHelloMessageString); /* Compare computed and received hvi */ if (memcmp(computedHvi, peerCommitMessageData->hvi, 32)!=0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHVI; } } } else { /* if we are initiator(we didn't received any commit message and then no H2), we must check that H3=SHA256(SHA256(H1)) and the Hello message MAC */ uint8_t checkH2[32]; uint8_t checkH3[32]; uint8_t checkMAC[32]; bzrtpHelloMessage_t *peerHelloMessageData; if (zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no Hello message in this channel, this DHPart1 shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerHelloMessageData = (bzrtpHelloMessage_t *)zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData; /* Check H3 = SHA256(SHA256(H1)) */ bctbx_sha256(messageData->H1, 32, 32, checkH2); bctbx_sha256(checkH2, 32, 32, checkH3); if (memcmp(checkH3, peerHelloMessageData->H3, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the hello MAC message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(checkH2, 32, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerHelloMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } } /* alloc pv once all check are passed */ messageData->pv = (uint8_t *)malloc(pvLength*sizeof(uint8_t)); memcpy(messageData->rs1ID, messageContent, 8); messageContent +=8; memcpy(messageData->rs2ID, messageContent, 8); messageContent +=8; memcpy(messageData->auxsecretID, messageContent, 8); messageContent +=8; memcpy(messageData->pbxsecretID, messageContent, 8); messageContent +=8; memcpy(messageData->pv, messageContent, pvLength); messageContent +=pvLength; memcpy(messageData->MAC, messageContent, 8); /* attach the message structure to the packet one */ zrtpPacket->messageData = (void *)messageData; /* the parsed packet must be saved as it is used to generate the total_hash */ zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t)); memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */ } break; /* MSGTYPE_DHPART1 and MSGTYPE_DHPART2 */ case MSGTYPE_CONFIRM1: case MSGTYPE_CONFIRM2: { uint8_t *confirmMessageKey = NULL; uint8_t *confirmMessageMacKey = NULL; bzrtpConfirmMessage_t *messageData; uint16_t cipherTextLength; uint8_t computedHmac[8]; uint8_t *confirmPlainMessageBuffer; uint8_t *confirmPlainMessage; /* we shall first decrypt and validate the message, check we have the keys to do it */ if (zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { /* responder uses initiator's keys to decrypt */ if ((zrtpChannelContext->zrtpkeyi == NULL) || (zrtpChannelContext->mackeyi == NULL)) { return BZRTP_PARSER_ERROR_INVALIDCONTEXT; } confirmMessageKey = zrtpChannelContext->zrtpkeyi; confirmMessageMacKey = zrtpChannelContext->mackeyi; } if (zrtpChannelContext->role == BZRTP_ROLE_INITIATOR) { /* the iniator uses responder's keys to decrypt */ if ((zrtpChannelContext->zrtpkeyr == NULL) || (zrtpChannelContext->mackeyr == NULL)) { return BZRTP_PARSER_ERROR_INVALIDCONTEXT; } confirmMessageKey = zrtpChannelContext->zrtpkeyr; confirmMessageMacKey = zrtpChannelContext->mackeyr; } /* allocate a confirm message structure */ messageData = (bzrtpConfirmMessage_t *)malloc(sizeof(bzrtpConfirmMessage_t)); /* get the mac and the IV */ memcpy(messageData->confirm_mac, messageContent, 8); messageContent +=8; memcpy(messageData->CFBIV, messageContent, 16); messageContent +=16; /* get the cipher text length */ cipherTextLength = zrtpPacket->messageLength - ZRTP_MESSAGE_HEADER_LENGTH - 24; /* confirm message is header, confirm_mac(8 bytes), CFB IV(16 bytes), encrypted part */ /* validate the mac over the cipher text */ zrtpChannelContext->hmacFunction(confirmMessageMacKey, zrtpChannelContext->hashLength, messageContent, cipherTextLength, 8, computedHmac); if (memcmp(computedHmac, messageData->confirm_mac, 8) != 0) { /* confirm_mac doesn't match */ free(messageData); return BZRTP_PARSER_ERROR_UNMATCHINGCONFIRMMAC; } /* get plain message */ confirmPlainMessageBuffer = (uint8_t *)malloc(cipherTextLength*sizeof(uint8_t)); zrtpChannelContext->cipherDecryptionFunction(confirmMessageKey, messageData->CFBIV, messageContent, cipherTextLength, confirmPlainMessageBuffer); confirmPlainMessage = confirmPlainMessageBuffer; /* point into the allocated buffer */ /* parse it */ memcpy(messageData->H0, confirmPlainMessage, 32); confirmPlainMessage +=33; /* +33 because next 8 bits are unused */ /* Hash chain checking: if we are in multichannel or shared mode, we had not DHPart and then no H1 */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh || zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult) { /* compute the H1=SHA256(H0) we never received */ uint8_t checkH1[32]; bctbx_sha256(messageData->H0, 32, 32, checkH1); /* if we are responder, we received a commit packet with H2 then check that H2=SHA256(H1) and that the commit message MAC keyed with H1 match */ if ( zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { uint8_t checkH2[32]; uint8_t checkMAC[32]; bzrtpCommitMessage_t *peerCommitMessageData; if (zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no Commit message in this channel, this Confirm2 shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerCommitMessageData = (bzrtpCommitMessage_t *)zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageData; /* Check H2 = SHA256(H1) */ bctbx_sha256(checkH1, 32, 32, checkH2); if (memcmp(checkH2, peerCommitMessageData->H2, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the Commit MAC message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(checkH1, 32, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerCommitMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } } else { /* if we are initiator(we didn't received any commit message and then no H2), we must check that H3=SHA256(SHA256(H1)) and the Hello message MAC */ uint8_t checkH2[32]; uint8_t checkH3[32]; uint8_t checkMAC[32]; bzrtpHelloMessage_t *peerHelloMessageData; if (zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no Hello message in this channel, this Confirm1 shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerHelloMessageData = (bzrtpHelloMessage_t *)zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData; /* Check H3 = SHA256(SHA256(H1)) */ bctbx_sha256(checkH1, 32, 32, checkH2); bctbx_sha256(checkH2, 32, 32, checkH3); if (memcmp(checkH3, peerHelloMessageData->H3, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the hello MAC message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(checkH2, 32, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerHelloMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } } } else { /* we are in DHM mode */ /* We have now H0, check it matches the H1 we had in the DHPart message H1=SHA256(H0) and that the DHPart message MAC is correct */ uint8_t checkH1[32]; uint8_t checkMAC[32]; bzrtpDHPartMessage_t *peerDHPartMessageData; if (zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID] == NULL) { free (messageData); /* we have no DHPART message in this channel, this confirm shall never have arrived, discard it as invalid */ return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } peerDHPartMessageData = (bzrtpDHPartMessage_t *)zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageData; /* Check H1 = SHA256(H0) */ bctbx_sha256(messageData->H0, 32, 32, checkH1); if (memcmp(checkH1, peerDHPartMessageData->H1, 32) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN; } /* Check the DHPart message. * MAC is 8 bytes long and is computed on the message(skip the ZRTP_PACKET_HEADER) and exclude the mac itself (-8 bytes from message Length) */ bctbx_hmacSha256(messageData->H0, 32, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength-8, 8, checkMAC); if (memcmp(checkMAC, peerDHPartMessageData->MAC, 8) != 0) { free (messageData); return BZRTP_PARSER_ERROR_UNMATCHINGMAC; } } messageData->sig_len = ((uint16_t)(confirmPlainMessage[0]&0x01))<<8 | (((uint16_t)confirmPlainMessage[1])&0x00FF); confirmPlainMessage += 2; messageData->E = ((*confirmPlainMessage)&0x08)>>3; messageData->V = ((*confirmPlainMessage)&0x04)>>2; messageData->A = ((*confirmPlainMessage)&0x02)>>1; messageData->D = (*confirmPlainMessage)&0x01; confirmPlainMessage += 1; messageData->cacheExpirationInterval = (((uint32_t)confirmPlainMessage[0])<<24) | (((uint32_t)confirmPlainMessage[1])<<16) | (((uint32_t)confirmPlainMessage[2])<<8) | ((uint32_t)confirmPlainMessage[3]); confirmPlainMessage += 4; /* if sig_len indicate a signature, parse it */ if (messageData->sig_len>0) { memcpy(messageData->signatureBlockType, confirmPlainMessage, 4); confirmPlainMessage += 4; /* allocate memory for the signature block, sig_len is in words(32 bits) and includes the signature block type word */ messageData->signatureBlock = (uint8_t *)malloc(4*(messageData->sig_len-1)*sizeof(uint8_t)); memcpy(messageData->signatureBlock, confirmPlainMessage, 4*(messageData->sig_len-1)); } else { messageData->signatureBlock = NULL; } /* free plain buffer */ free(confirmPlainMessageBuffer); /* the parsed commit packet must be saved as it is used to check correct packet repetition */ zrtpPacket->packetString = (uint8_t *)malloc(inputLength*sizeof(uint8_t)); memcpy(zrtpPacket->packetString, input, inputLength); /* store the whole packet even if we may use the message only */ /* attach the message structure to the packet one */ zrtpPacket->messageData = (void *)messageData; } break; /* MSGTYPE_CONFIRM1 and MSGTYPE_CONFIRM2 */ case MSGTYPE_CONF2ACK: /* nothing to do for this one */ break; /* MSGTYPE_CONF2ACK */ case MSGTYPE_PING: { /* allocate a ping message structure */ bzrtpPingMessage_t *messageData; messageData = (bzrtpPingMessage_t *)malloc(sizeof(bzrtpPingMessage_t)); /* fill the structure */ memcpy(messageData->version, messageContent, 4); messageContent +=4; memcpy(messageData->endpointHash, messageContent, 8); /* attach the message structure to the packet one */ zrtpPacket->messageData = (void *)messageData; } break; /* MSGTYPE_PING */ } return 0; } /* Create the packet string from the messageData contained into the zrtp Packet structure */ int bzrtp_packetBuild(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket, uint16_t sequenceNumber) { int i; uint8_t *messageTypeString; uint8_t *messageString = NULL; /* will point directly to the begining of the message within the packetString buffer */ uint8_t *MACbuffer = NULL; /* if needed this will point to the beginin of the MAC in the packetString buffer */ /*uint8_t *MACMessageData = NULL; */ /* if needed this will point to the MAC field in the message Data structure */ uint8_t *MACkey = NULL; /* checks */ if (zrtpPacket==NULL) { return BZRTP_BUILDER_ERROR_INVALIDPACKET; } /* get the message type (and check it is valid) */ messageTypeString = messageTypeInttoString(zrtpPacket->messageType); if (messageTypeString == NULL) { return BZRTP_BUILDER_ERROR_INVALIDMESSAGETYPE; } /* create first the message. Header and CRC will be added afterward */ switch (zrtpPacket->messageType) { case MSGTYPE_HELLO : { bzrtpHelloMessage_t *messageData; /* get the Hello message structure */ if (zrtpPacket->messageData == NULL) { return BZRTP_BUILDER_ERROR_INVALIDMESSAGE; } messageData = (bzrtpHelloMessage_t *)zrtpPacket->messageData; /* compute the message length in bytes : fixed length and optionnal algorithms parts */ zrtpPacket->messageLength = ZRTP_HELLOMESSAGE_FIXED_LENGTH + 4*((uint16_t)(messageData->hc)+(uint16_t)(messageData->cc)+(uint16_t)(messageData->ac)+(uint16_t)(messageData->kc)+(uint16_t)(messageData->sc)); /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+zrtpPacket->messageLength+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); /* have the messageString pointer to the begining of message(after the message header wich is computed for all messages after the switch) * within the packetString buffer*/ messageString = zrtpPacket->packetString + ZRTP_PACKET_HEADER_LENGTH + ZRTP_MESSAGE_HEADER_LENGTH; /* set the version (shall be 1.10), Client identifier, H3, ZID, S,M,P flags and hc,cc,ac,kc,sc */ memcpy(messageString, messageData->version, 4); messageString += 4; memcpy(messageString, messageData->clientIdentifier, 16); messageString += 16; memcpy(messageString, messageData->H3, 32); messageString += 32; memcpy(messageString, messageData->ZID, 12); messageString += 12; *messageString = ((((messageData->S)&0x01)<<6) | (((messageData->M)&0x01)<<5) | (((messageData->P)&0x01)<<4))&0x70; messageString += 1; *messageString = (messageData->hc)&0x0F; messageString += 1; *messageString = (((messageData->cc)<<4)&0xF0) | ((messageData->ac)&0x0F) ; messageString += 1; *messageString = (((messageData->kc)<<4)&0xF0) | ((messageData->sc)&0x0F) ; messageString += 1; /* now set optionnal supported algorithms */ for (i=0; ihc; i++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedHash[i], messageString); messageString +=4; } for (i=0; icc; i++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedCipher[i], messageString); messageString +=4; } for (i=0; iac; i++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedAuthTag[i], messageString); messageString +=4; } for (i=0; ikc; i++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedKeyAgreement[i], messageString); messageString +=4; } for (i=0; isc; i++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedSas[i], messageString); messageString +=4; } /* there is a MAC to compute, set the pointers to the key and MAC output buffer */ MACbuffer = messageString; MACkey = zrtpChannelContext->selfH[2]; /* HMAC of Hello packet is keyed by H2 which have been set at context initialising */ } break; /* MSGTYPE_HELLO */ case MSGTYPE_HELLOACK : { /* the message length is fixed */ zrtpPacket->messageLength = ZRTP_HELLOACKMESSAGE_FIXED_LENGTH; /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+ZRTP_HELLOACKMESSAGE_FIXED_LENGTH+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); } break; /* MSGTYPE_HELLOACK */ case MSGTYPE_COMMIT : { bzrtpCommitMessage_t *messageData; uint16_t variableLength = 0; /* get the Commit message structure */ if (zrtpPacket->messageData == NULL) { return BZRTP_BUILDER_ERROR_INVALIDMESSAGE; } messageData = (bzrtpCommitMessage_t *)zrtpPacket->messageData; /* compute message length */ switch(messageData->keyAgreementAlgo) { case ZRTP_KEYAGREEMENT_DH2k : case ZRTP_KEYAGREEMENT_EC25 : case ZRTP_KEYAGREEMENT_X255 : case ZRTP_KEYAGREEMENT_X448 : case ZRTP_KEYAGREEMENT_DH3k : case ZRTP_KEYAGREEMENT_EC38 : case ZRTP_KEYAGREEMENT_EC52 : variableLength = 32; /* hvi is 32 bytes length in DH Commit message format */ break; case ZRTP_KEYAGREEMENT_Prsh : variableLength = 24; /* nonce (16 bytes) and keyID(8 bytes) are 24 bytes length in preshared Commit message format */ break; case ZRTP_KEYAGREEMENT_Mult : variableLength = 16; /* nonce is 24 bytes length in multistream Commit message format */ break; default: return BZRTP_BUILDER_ERROR_INVALIDMESSAGE; } zrtpPacket->messageLength = ZRTP_COMMITMESSAGE_FIXED_LENGTH + variableLength; /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+zrtpPacket->messageLength+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); /* have the messageString pointer to the begining of message(after the message header wich is computed for all messages after the switch) * within the packetString buffer*/ messageString = zrtpPacket->packetString + ZRTP_PACKET_HEADER_LENGTH + ZRTP_MESSAGE_HEADER_LENGTH; /* now insert the different message parts into the packetString */ memcpy(messageString, messageData->H2, 32); messageString += 32; memcpy(messageString, messageData->ZID, 12); messageString += 12; bzrtp_cryptoAlgoTypeIntToString(messageData->hashAlgo, messageString); messageString += 4; bzrtp_cryptoAlgoTypeIntToString(messageData->cipherAlgo, messageString); messageString += 4; bzrtp_cryptoAlgoTypeIntToString(messageData->authTagAlgo, messageString); messageString += 4; bzrtp_cryptoAlgoTypeIntToString(messageData->keyAgreementAlgo, messageString); messageString += 4; bzrtp_cryptoAlgoTypeIntToString(messageData->sasAlgo, messageString); messageString += 4; /* if it is a multistream or preshared commit insert the 16 bytes nonce */ if ((messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { memcpy(messageString, messageData->nonce, 16); messageString += 16; /* and the keyID for preshared commit only */ if (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { memcpy(messageString, messageData->keyID, 8); messageString +=8; } } else { /* it's a DH commit message, set the hvi */ memcpy(messageString, messageData->hvi, 32); messageString +=32; } /* there is a MAC to compute, set the pointers to the key and MAC output buffer */ MACbuffer = messageString; MACkey = zrtpChannelContext->selfH[1]; /* HMAC of Hello packet is keyed by H1 which have been set at context initialising */ } break; /*MSGTYPE_COMMIT */ case MSGTYPE_DHPART1 : case MSGTYPE_DHPART2 : { bzrtpDHPartMessage_t *messageData; uint16_t pvLength; /* get the DHPart message structure */ if (zrtpPacket->messageData == NULL) { return BZRTP_BUILDER_ERROR_INVALIDMESSAGE; } messageData = (bzrtpDHPartMessage_t *)zrtpPacket->messageData; /* compute message length */ pvLength = computeKeyAgreementPrivateValueLength(zrtpChannelContext->keyAgreementAlgo); if (pvLength==0) { return BZRTP_BUILDER_ERROR_INVALIDCONTEXT; } zrtpPacket->messageLength = ZRTP_DHPARTMESSAGE_FIXED_LENGTH + pvLength; /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+zrtpPacket->messageLength+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); /* have the messageString pointer to the begining of message(after the message header wich is computed for all messages after the switch) * within the packetString buffer*/ messageString = zrtpPacket->packetString + ZRTP_PACKET_HEADER_LENGTH + ZRTP_MESSAGE_HEADER_LENGTH; /* now insert the different message parts into the packetString */ memcpy(messageString, messageData->H1, 32); messageString += 32; memcpy(messageString, messageData->rs1ID, 8); messageString += 8; memcpy(messageString, messageData->rs2ID, 8); messageString += 8; memcpy(messageString, messageData->auxsecretID, 8); messageString += 8; memcpy(messageString, messageData->pbxsecretID, 8); messageString += 8; memcpy(messageString, messageData->pv, pvLength); messageString += pvLength; /* there is a MAC to compute, set the pointers to the key and MAC output buffer */ MACbuffer = messageString; MACkey = zrtpChannelContext->selfH[0]; /* HMAC of Hello packet is keyed by H0 which have been set at context initialising */ } break; /* MSGTYPE_DHPART1 and 2 */ case MSGTYPE_CONFIRM1: case MSGTYPE_CONFIRM2: { uint8_t *confirmMessageKey = NULL; uint8_t *confirmMessageMacKey = NULL; bzrtpConfirmMessage_t *messageData; uint16_t encryptedPartLength; uint8_t *plainMessageString; uint16_t plainMessageStringIndex = 0; /* we will have to encrypt and validate the message, check we have the keys to do it */ if (zrtpChannelContext->role == BZRTP_ROLE_INITIATOR) { if ((zrtpChannelContext->zrtpkeyi == NULL) || (zrtpChannelContext->mackeyi == NULL)) { return BZRTP_BUILDER_ERROR_INVALIDCONTEXT; } confirmMessageKey = zrtpChannelContext->zrtpkeyi; confirmMessageMacKey = zrtpChannelContext->mackeyi; } if (zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { if ((zrtpChannelContext->zrtpkeyr == NULL) || (zrtpChannelContext->mackeyr == NULL)) { return BZRTP_BUILDER_ERROR_INVALIDCONTEXT; } confirmMessageKey = zrtpChannelContext->zrtpkeyr; confirmMessageMacKey = zrtpChannelContext->mackeyr; } /* get the Confirm message structure */ if (zrtpPacket->messageData == NULL) { return BZRTP_BUILDER_ERROR_INVALIDMESSAGE; } messageData = (bzrtpConfirmMessage_t *)zrtpPacket->messageData; /* compute message length */ zrtpPacket->messageLength = ZRTP_CONFIRMMESSAGE_FIXED_LENGTH + messageData->sig_len*4; /* sig_len is in word of 4 bytes */ /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+zrtpPacket->messageLength+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); /* have the messageString pointer to the begining of message(after the message header wich is computed for all messages after the switch) * within the packetString buffer*/ messageString = zrtpPacket->packetString + ZRTP_PACKET_HEADER_LENGTH + ZRTP_MESSAGE_HEADER_LENGTH; /* allocate a temporary buffer to store the plain text */ encryptedPartLength = zrtpPacket->messageLength - ZRTP_MESSAGE_HEADER_LENGTH - 24; /* message header, confirm_mac(8 bytes) and CFB IV(16 bytes) are not encrypted */ plainMessageString = (uint8_t *)malloc(encryptedPartLength*sizeof(uint8_t)); /* fill the plain message buffer with data from the message structure */ memcpy(plainMessageString, messageData->H0, 32); plainMessageStringIndex += 32; plainMessageString[plainMessageStringIndex++] = 0x00; plainMessageString[plainMessageStringIndex++] = (uint8_t)(((messageData->sig_len)>>8)&0x0001); plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->sig_len)&0x00FF); plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->E&0x01)<<3) | (uint8_t)((messageData->V&0x01)<<2) | (uint8_t)((messageData->A&0x01)<<1) | (uint8_t)(messageData->D&0x01) ; /* cache expiration in a 32 bits unsigned int */ plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->cacheExpirationInterval>>24)&0xFF); plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->cacheExpirationInterval>>16)&0xFF); plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->cacheExpirationInterval>>8)&0xFF); plainMessageString[plainMessageStringIndex++] = (uint8_t)((messageData->cacheExpirationInterval)&0xFF); if (messageData->sig_len>0) { memcpy(plainMessageString+plainMessageStringIndex, messageData->signatureBlockType, 4); plainMessageStringIndex += 4; /* sig_len is in 4 bytes words and include the 1 word of signature block type */ memcpy(plainMessageString+plainMessageStringIndex, messageData->signatureBlock, (messageData->sig_len-1)*4); } /* encrypt the buffer, set the output directly in the messageString buffer at the correct position(+24 after message header) */ zrtpChannelContext->cipherEncryptionFunction(confirmMessageKey, messageData->CFBIV, plainMessageString, encryptedPartLength, messageString+24); free(plainMessageString); /* free the plain message string temporary buffer */ /* compute the mac over the encrypted part of the message and set the result in the messageString */ zrtpChannelContext->hmacFunction(confirmMessageMacKey, zrtpChannelContext->hashLength, messageString+24, encryptedPartLength, 8, messageString); messageString += 8; /* add the CFB IV */ memcpy(messageString, messageData->CFBIV, 16); } break; /* MSGTYPE_CONFIRM1 and MSGTYPE_CONFIRM2 */ case MSGTYPE_CONF2ACK: { /* the message length is fixed */ zrtpPacket->messageLength = ZRTP_CONF2ACKMESSAGE_FIXED_LENGTH; /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+ZRTP_CONF2ACKMESSAGE_FIXED_LENGTH+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); } break; /* MSGTYPE_CONF2ACK */ case MSGTYPE_PINGACK: { bzrtpPingAckMessage_t *messageData; /* the message length is fixed */ zrtpPacket->messageLength = ZRTP_PINGACKMESSAGE_FIXED_LENGTH; /* allocate the packetString buffer : packet is header+message+crc */ zrtpPacket->packetString = (uint8_t *)malloc((ZRTP_PACKET_HEADER_LENGTH+ZRTP_PINGACKMESSAGE_FIXED_LENGTH+ZRTP_PACKET_CRC_LENGTH)*sizeof(uint8_t)); messageString = zrtpPacket->packetString + ZRTP_PACKET_HEADER_LENGTH + ZRTP_MESSAGE_HEADER_LENGTH; /* now insert the different message parts into the packetString */ messageData = (bzrtpPingAckMessage_t *)zrtpPacket->messageData; memcpy(messageString, messageData->version, 4); messageString += 4; memcpy(messageString, messageData->endpointHash, 8); messageString += 8; memcpy(messageString, messageData->endpointHashReceived, 8); messageString += 8; *messageString++ = (uint8_t)((messageData->SSRC>>24)&0xFF); *messageString++ = (uint8_t)((messageData->SSRC>>16)&0xFF); *messageString++ = (uint8_t)((messageData->SSRC>>8)&0xFF); *messageString++ = (uint8_t)(messageData->SSRC&0xFF); } break; /* MSGTYPE_PINGACK */ } /* write headers only if we have a packet string */ if (zrtpPacket->packetString != NULL) { uint32_t CRC; uint8_t *CRCbuffer; zrtpMessageSetHeader(zrtpPacket->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpPacket->messageLength, messageTypeString); /* Do we have a MAC to compute on the message ? */ if (MACbuffer != NULL) { /* compute the MAC(64 bits only) using the implicit HMAC function for ZRTP v1.10: HMAC-SHA256 */ /* HMAC is computed on the whole message except the MAC itself so a length of zrtpPacket->messageLength-8 */ bctbx_hmacSha256(MACkey, 32, zrtpPacket->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpPacket->messageLength-8, 8, MACbuffer); } /* set packet header and CRC */ /* preambule */ zrtpPacket->packetString[0] = 0x10; zrtpPacket->packetString[1] = 0x00; /* Sequence number */ zrtpPacket->packetString[2] = (uint8_t)((sequenceNumber>>8)&0x00FF); zrtpPacket->packetString[3] = (uint8_t)(sequenceNumber&0x00FF); /* ZRTP magic cookie */ zrtpPacket->packetString[4] = (uint8_t)((ZRTP_MAGIC_COOKIE>>24)&0xFF); zrtpPacket->packetString[5] = (uint8_t)((ZRTP_MAGIC_COOKIE>>16)&0xFF); zrtpPacket->packetString[6] = (uint8_t)((ZRTP_MAGIC_COOKIE>>8)&0xFF); zrtpPacket->packetString[7] = (uint8_t)(ZRTP_MAGIC_COOKIE&0xFF); /* Source Identifier */ zrtpPacket->packetString[8] = (uint8_t)(((zrtpPacket->sourceIdentifier)>>24)&0xFF); zrtpPacket->packetString[9] = (uint8_t)(((zrtpPacket->sourceIdentifier)>>16)&0xFF); zrtpPacket->packetString[10] = (uint8_t)(((zrtpPacket->sourceIdentifier)>>8)&0xFF); zrtpPacket->packetString[11] = (uint8_t)((zrtpPacket->sourceIdentifier)&0xFF); /* CRC */ CRC = bzrtp_CRC32(zrtpPacket->packetString, zrtpPacket->messageLength+ZRTP_PACKET_HEADER_LENGTH); CRCbuffer = (zrtpPacket->packetString)+(zrtpPacket->messageLength)+ZRTP_PACKET_HEADER_LENGTH; *CRCbuffer = (uint8_t)((CRC>>24)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>16)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>8)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)(CRC&0xFF); return 0; } else { /* no packetString allocated something wen't wrong but we shall never arrive here */ return BZRTP_BUILDER_ERROR_UNKNOWN; } } /* create a zrtpPacket and initialise it's structures */ bzrtpPacket_t *bzrtp_createZrtpPacket(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, uint32_t messageType, int *exitCode) { /* allocate packet */ bzrtpPacket_t *zrtpPacket = (bzrtpPacket_t *)malloc(sizeof(bzrtpPacket_t)); memset(zrtpPacket, 0, sizeof(bzrtpPacket_t)); zrtpPacket->messageData = NULL; zrtpPacket->packetString = NULL; /* initialise it */ switch(messageType) { case MSGTYPE_HELLO: { int i; bzrtpHelloMessage_t *zrtpHelloMessage = (bzrtpHelloMessage_t *)malloc(sizeof(bzrtpHelloMessage_t)); memset(zrtpHelloMessage, 0, sizeof(bzrtpHelloMessage_t)); /* initialise some fields using zrtp context data */ memcpy(zrtpHelloMessage->version, ZRTP_VERSION, 4); strncpy((char*)zrtpHelloMessage->clientIdentifier, ZRTP_CLIENT_IDENTIFIER, 16); zrtpHelloMessage->clientIdentifier[16]='\0'; /* be sure the clientIdentifier filed is a NULL terminated string */ memcpy(zrtpHelloMessage->H3, zrtpChannelContext->selfH[3], 32); memcpy(zrtpHelloMessage->ZID, zrtpContext->selfZID, 12); /* set all S,M,P flags to zero as we're not able to verify signatures, we're not a PBX(TODO: implement?), we're not passive */ zrtpHelloMessage->S = 0; zrtpHelloMessage->M = 0; zrtpHelloMessage->P = 0; /* get the algorithm availabilities from the context */ zrtpHelloMessage->hc = zrtpContext->hc; zrtpHelloMessage->cc = zrtpContext->cc; zrtpHelloMessage->ac = zrtpContext->ac; zrtpHelloMessage->kc = zrtpContext->kc; zrtpHelloMessage->sc = zrtpContext->sc; for (i=0; ihc; i++) { zrtpHelloMessage->supportedHash[i] = zrtpContext->supportedHash[i]; } for (i=0; icc; i++) { zrtpHelloMessage->supportedCipher[i] = zrtpContext->supportedCipher[i]; } for (i=0; iac; i++) { zrtpHelloMessage->supportedAuthTag[i] = zrtpContext->supportedAuthTag[i]; } for (i=0; ikc; i++) { zrtpHelloMessage->supportedKeyAgreement[i] = zrtpContext->supportedKeyAgreement[i]; } for (i=0; isc; i++) { zrtpHelloMessage->supportedSas[i] = zrtpContext->supportedSas[i]; } /* attach the message data to the packet */ zrtpPacket->messageData = zrtpHelloMessage; } break; /* MSGTYPE_HELLO */ case MSGTYPE_HELLOACK : { /* nothing to do for the Hello ACK packet as it just contains it's type */ } break; /* MSGTYPE_HELLOACK */ /* In case of DH commit, this one must be called after the DHPart build and the self DH message and peer Hello message are stored in the context */ case MSGTYPE_COMMIT : { bzrtpCommitMessage_t *zrtpCommitMessage = (bzrtpCommitMessage_t *)malloc(sizeof(bzrtpCommitMessage_t)); memset(zrtpCommitMessage, 0, sizeof(bzrtpCommitMessage_t)); /* initialise some fields using zrtp context data */ memcpy(zrtpCommitMessage->H2, zrtpChannelContext->selfH[2], 32); memcpy(zrtpCommitMessage->ZID, zrtpContext->selfZID, 12); zrtpCommitMessage->hashAlgo = zrtpChannelContext->hashAlgo; zrtpCommitMessage->cipherAlgo = zrtpChannelContext->cipherAlgo; zrtpCommitMessage->authTagAlgo = zrtpChannelContext->authTagAlgo; zrtpCommitMessage->keyAgreementAlgo = zrtpChannelContext->keyAgreementAlgo; zrtpCommitMessage->sasAlgo = zrtpChannelContext->sasAlgo; /* if it is a multistream or preshared commit create a 16 random bytes nonce */ if ((zrtpCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (zrtpCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { bctbx_rng_get(zrtpContext->RNGContext, zrtpCommitMessage->nonce, 16); /* and the keyID for preshared commit only */ if (zrtpCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { /* TODO at this point we must first compute the preShared key - make sure at least rs1 is present */ /* preshared_key = hash(len(rs1) || rs1 || len(auxsecret) || auxsecret || len(pbxsecret) || pbxsecret) using the agreed hash and store it into the env */ /* and then the keyID : MAC(preshared_key, "Prsh") truncated to 64 bits using the agreed MAC */ } } else { /* it's a DH commit message, set the hvi */ /* hvi = hash(initiator's DHPart2 message || responder's Hello message) using the agreed hash function truncated to 256 bits */ /* create a string with the messages concatenated */ uint16_t DHPartMessageLength = zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength; uint16_t HelloMessageLength = zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength; uint16_t DHPartHelloMessageStringLength = DHPartMessageLength + HelloMessageLength; uint8_t *DHPartHelloMessageString = (uint8_t *)malloc(DHPartHelloMessageStringLength*sizeof(uint8_t)); memcpy(DHPartHelloMessageString, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, DHPartMessageLength); memcpy(DHPartHelloMessageString+DHPartMessageLength, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, HelloMessageLength); zrtpChannelContext->hashFunction(DHPartHelloMessageString, DHPartHelloMessageStringLength, 32, zrtpCommitMessage->hvi); free(DHPartHelloMessageString); } /* attach the message data to the packet */ zrtpPacket->messageData = zrtpCommitMessage; } break; /* MSGTYPE_COMMIT */ /* this one is called after the exchange of Hello messages when the crypto algo agreement have been performed */ case MSGTYPE_DHPART1 : case MSGTYPE_DHPART2 : { uint8_t secretLength; /* is in bytes */ uint8_t bctbx_keyAgreementAlgo = BCTBX_DHM_UNSET; bzrtpDHPartMessage_t *zrtpDHPartMessage = (bzrtpDHPartMessage_t *)malloc(sizeof(bzrtpDHPartMessage_t)); memset(zrtpDHPartMessage, 0, sizeof(bzrtpDHPartMessage_t)); /* initialise some fields using zrtp context data */ memcpy(zrtpDHPartMessage->H1, zrtpChannelContext->selfH[1], 32); /* get the retained secret from context, we anyway create a DHPart2 packet that we may turn into a DHPart1 packet if we end to * be the responder and not the initiator, use the initiator retained secret hashes */ memcpy(zrtpDHPartMessage->rs1ID, zrtpContext->initiatorCachedSecretHash.rs1ID, 8); memcpy(zrtpDHPartMessage->rs2ID, zrtpContext->initiatorCachedSecretHash.rs2ID, 8); memcpy(zrtpDHPartMessage->auxsecretID, zrtpChannelContext->initiatorAuxsecretID, 8); memcpy(zrtpDHPartMessage->pbxsecretID, zrtpContext->initiatorCachedSecretHash.pbxsecretID, 8); /* compute the public value and insert it in the message, will then be used whatever role - initiator or responder - we assume */ /* initialise the dhm context, secret length shall be twice the size of cipher block key length - rfc section 5.1.5 */ switch (zrtpChannelContext->cipherAlgo) { case ZRTP_CIPHER_AES3: case ZRTP_CIPHER_2FS3: secretLength = 64; break; case ZRTP_CIPHER_AES2: case ZRTP_CIPHER_2FS2: secretLength = 48; break; case ZRTP_CIPHER_AES1: case ZRTP_CIPHER_2FS1: default: secretLength = 32; break; } switch (zrtpChannelContext->keyAgreementAlgo) { case ZRTP_KEYAGREEMENT_DH2k: case ZRTP_KEYAGREEMENT_DH3k: { bctbx_DHMContext_t *DHMContext = NULL; if (zrtpChannelContext->keyAgreementAlgo==ZRTP_KEYAGREEMENT_DH2k) { bctbx_keyAgreementAlgo = BCTBX_DHM_2048; } else { bctbx_keyAgreementAlgo = BCTBX_DHM_3072; } /* create DHM context */ DHMContext = (void *)bctbx_CreateDHMContext(bctbx_keyAgreementAlgo, secretLength); if (DHMContext == NULL) { free(zrtpPacket); free(zrtpDHPartMessage); *exitCode = BZRTP_CREATE_ERROR_UNABLETOCREATECRYPTOCONTEXT; return NULL; } /* create private key and compute the public value */ bctbx_DHMCreatePublic(DHMContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, zrtpContext->RNGContext); zrtpDHPartMessage->pv = (uint8_t *)malloc((zrtpChannelContext->keyAgreementLength)*sizeof(uint8_t)); memcpy(zrtpDHPartMessage->pv, DHMContext->self, zrtpChannelContext->keyAgreementLength); zrtpContext->keyAgreementContext = (void *)DHMContext; /* save DHM context in zrtp Context */ zrtpContext->keyAgreementAlgo = zrtpChannelContext->keyAgreementAlgo; /* store algo in global context to be able to destroy it correctly*/ } break; case ZRTP_KEYAGREEMENT_X255: case ZRTP_KEYAGREEMENT_X448: { bctbx_ECDHContext_t *ECDHContext = NULL; if (zrtpChannelContext->keyAgreementAlgo==ZRTP_KEYAGREEMENT_X255) { bctbx_keyAgreementAlgo = BCTBX_ECDH_X25519; } else { bctbx_keyAgreementAlgo = BCTBX_ECDH_X448; } /* Create the ECDH context */ ECDHContext = (void *)bctbx_CreateECDHContext(bctbx_keyAgreementAlgo); if (ECDHContext == NULL) { free(zrtpPacket); free(zrtpDHPartMessage); *exitCode = BZRTP_CREATE_ERROR_UNABLETOCREATECRYPTOCONTEXT; return NULL; } /* create private key and compute the public value */ bctbx_ECDHCreateKeyPair(ECDHContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, zrtpContext->RNGContext); zrtpDHPartMessage->pv = (uint8_t *)malloc((zrtpChannelContext->keyAgreementLength)*sizeof(uint8_t)); memcpy(zrtpDHPartMessage->pv, ECDHContext->selfPublic, zrtpChannelContext->keyAgreementLength); zrtpContext->keyAgreementContext = (void *)ECDHContext; /* save ECDH context in zrtp Context */ zrtpContext->keyAgreementAlgo = zrtpChannelContext->keyAgreementAlgo; /* store algo in global context to be able to destroy it correctly*/ } break; default: free(zrtpPacket); free(zrtpDHPartMessage); *exitCode = BZRTP_CREATE_ERROR_UNABLETOCREATECRYPTOCONTEXT; return NULL; break; } /* attach the message data to the packet */ zrtpPacket->messageData = zrtpDHPartMessage; } break; /* MSGTYPE_DHPART1 and MSGTYPE_DHPART2 */ case MSGTYPE_CONFIRM1: case MSGTYPE_CONFIRM2: { bzrtpConfirmMessage_t *zrtpConfirmMessage = (bzrtpConfirmMessage_t *)malloc(sizeof(bzrtpConfirmMessage_t)); memset(zrtpConfirmMessage, 0, sizeof(bzrtpConfirmMessage_t)); /* initialise some fields using zrtp context data */ memcpy(zrtpConfirmMessage->H0, zrtpChannelContext->selfH[0], 32); zrtpConfirmMessage->sig_len = 0; /* signature is not supported */ zrtpConfirmMessage->cacheExpirationInterval = 0xFFFFFFFF; /* expiration interval is set to unlimited as recommended in rfc section 4.9 */ zrtpConfirmMessage->E = 0; /* we are not a PBX and then will never signal an enrollment - rfc section 7.3.1 */ zrtpConfirmMessage->V = zrtpContext->cachedSecret.previouslyVerifiedSas; zrtpConfirmMessage->A = 0; /* Go clear message is not supported - rfc section 4.7.2 */ zrtpConfirmMessage->D = 0; /* The is no backdoor in our implementation of ZRTP - rfc section 11 */ /* generate a random CFB IV */ bctbx_rng_get(zrtpContext->RNGContext, zrtpConfirmMessage->CFBIV, 16); /* attach the message data to the packet */ zrtpPacket->messageData = zrtpConfirmMessage; } break; /* MSGTYPE_CONFIRM1 and MSGTYPE_CONFIRM2 */ case MSGTYPE_CONF2ACK : { /* nothing to do for the conf2ACK packet as it just contains it's type */ } break; /* MSGTYPE_CONF2ACK */ case MSGTYPE_PINGACK: { bzrtpPingMessage_t *pingMessage; bzrtpPingAckMessage_t *zrtpPingAckMessage; /* to create a pingACK we must have a ping packet in the channel context, check it */ bzrtpPacket_t *pingPacket = zrtpChannelContext->pingPacket; if (pingPacket == NULL) { *exitCode = BZRTP_CREATE_ERROR_INVALIDCONTEXT; return NULL; } pingMessage = (bzrtpPingMessage_t *)pingPacket->messageData; /* create the message */ zrtpPingAckMessage = (bzrtpPingAckMessage_t *)malloc(sizeof(bzrtpPingAckMessage_t)); memset(zrtpPingAckMessage, 0, sizeof(bzrtpPingAckMessage_t)); /* initialise all fields using zrtp context data and the received ping message */ memcpy(zrtpPingAckMessage->version,ZRTP_VERSION , 4); /* we support version 1.10 only, so no need to even check what was sent in the ping */ memcpy(zrtpPingAckMessage->endpointHash, zrtpContext->selfZID, 8); /* as suggested in rfc section 5.16, use the truncated ZID as endPoint hash */ memcpy(zrtpPingAckMessage->endpointHashReceived, pingMessage->endpointHash, 8); zrtpPingAckMessage->SSRC = pingPacket->sourceIdentifier; /* attach the message data to the packet */ zrtpPacket->messageData = zrtpPingAckMessage; } /* MSGTYPE_PINGACK */ break; default: free(zrtpPacket); *exitCode = BZRTP_CREATE_ERROR_INVALIDMESSAGETYPE; return NULL; break; } zrtpPacket->sequenceNumber = 0; /* this field is not used buy the packet creator, sequence number is given as a parameter when converting the message to a packet string(packet build). Used only when parsing a string into a packet struct */ zrtpPacket->messageType = messageType; zrtpPacket->sourceIdentifier = zrtpChannelContext->selfSSRC; zrtpPacket->messageLength = 0; /* length will be computed at packet build */ zrtpPacket->packetString = NULL; *exitCode=0; return zrtpPacket; } void bzrtp_freeZrtpPacket(bzrtpPacket_t *zrtpPacket) { if (zrtpPacket != NULL) { /* some messages have fields to be freed */ if (zrtpPacket->messageData != NULL) { switch(zrtpPacket->messageType) { case MSGTYPE_DHPART1 : case MSGTYPE_DHPART2 : { bzrtpDHPartMessage_t *typedMessageData = (bzrtpDHPartMessage_t *)(zrtpPacket->messageData); if (typedMessageData != NULL) { free(typedMessageData->pv); } } break; case MSGTYPE_CONFIRM1: case MSGTYPE_CONFIRM2: { bzrtpConfirmMessage_t *typedMessageData = (bzrtpConfirmMessage_t *)(zrtpPacket->messageData); if (typedMessageData != NULL) { free(typedMessageData->signatureBlock); } } break; } } free(zrtpPacket->messageData); free(zrtpPacket->packetString); free(zrtpPacket); } } /** * @brief Modify the current sequence number of the packet in the packetString and sequenceNumber fields * The CRC at the end of packetString is also updated * * param[in/out] zrtpPacket The zrtpPacket to modify, the packetString must have been generated by * a call to bzrtp_packetBuild on this packet * param[in] sequenceNumber The new sequence number to insert in the packetString * * return 0 on succes, error code otherwise */ int bzrtp_packetUpdateSequenceNumber(bzrtpPacket_t *zrtpPacket, uint16_t sequenceNumber) { uint32_t CRC; uint8_t *CRCbuffer; if (zrtpPacket == NULL) { return BZRTP_BUILDER_ERROR_INVALIDPACKET; } if (zrtpPacket->packetString == NULL) { return BZRTP_BUILDER_ERROR_INVALIDPACKET; } /* update the sequence number field (even if it is probably useless as this function is called just before sending the DHPart2 packet only)*/ zrtpPacket->sequenceNumber = sequenceNumber; /* update hte sequence number in the packetString */ *(zrtpPacket->packetString+2)= (uint8_t)((sequenceNumber>>8)&0x00FF); *(zrtpPacket->packetString+3)= (uint8_t)(sequenceNumber&0x00FF); /* update the CRC */ CRC = bzrtp_CRC32(zrtpPacket->packetString, zrtpPacket->messageLength+ZRTP_PACKET_HEADER_LENGTH); CRCbuffer = (zrtpPacket->packetString)+(zrtpPacket->messageLength)+ZRTP_PACKET_HEADER_LENGTH; *CRCbuffer = (uint8_t)((CRC>>24)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>16)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>8)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)(CRC&0xFF); return 0; } /*** Local functions implementation ***/ uint8_t *messageTypeInttoString(uint32_t messageType) { switch(messageType) { case MSGTYPE_HELLO : return (uint8_t *)"Hello "; break; case MSGTYPE_HELLOACK : return (uint8_t *)"HelloACK"; break; case MSGTYPE_COMMIT : return (uint8_t *)"Commit "; break; case MSGTYPE_DHPART1 : return (uint8_t *)"DHPart1 "; break; case MSGTYPE_DHPART2 : return (uint8_t *)"DHPart2 "; break; case MSGTYPE_CONFIRM1 : return (uint8_t *)"Confirm1"; break; case MSGTYPE_CONFIRM2 : return (uint8_t *)"Confirm2"; break; case MSGTYPE_CONF2ACK : return (uint8_t *)"Conf2ACK"; break; case MSGTYPE_ERROR : return (uint8_t *)"Error "; break; case MSGTYPE_ERRORACK : return (uint8_t *)"ErrorACK"; break; case MSGTYPE_GOCLEAR : return (uint8_t *)"GoClear "; break; case MSGTYPE_CLEARACK : return (uint8_t *)"ClearACK"; break; case MSGTYPE_SASRELAY : return (uint8_t *)"SASrelay"; break; case MSGTYPE_RELAYACK : return (uint8_t *)"RelayACK"; break; case MSGTYPE_PING : return (uint8_t *)"Ping "; break; case MSGTYPE_PINGACK : return (uint8_t *)"PingACK "; break; } return NULL; } /* * @brief Map the 8 char string value message type to an int32_t * * @param[in] messageTypeString an 8 bytes string matching a zrtp message type * * @return a 32-bits unsigned integer mapping the message type */ int32_t messageTypeStringtoInt(uint8_t messageTypeString[8]) { if (memcmp(messageTypeString, "Hello ", 8) == 0) { return MSGTYPE_HELLO; } else if (memcmp(messageTypeString, "HelloACK", 8) == 0) { return MSGTYPE_HELLOACK; } else if (memcmp(messageTypeString, "Commit ", 8) == 0) { return MSGTYPE_COMMIT; } else if (memcmp(messageTypeString, "DHPart1 ", 8) == 0) { return MSGTYPE_DHPART1; } else if (memcmp(messageTypeString, "DHPart2 ", 8) == 0) { return MSGTYPE_DHPART2; } else if (memcmp(messageTypeString, "Confirm1", 8) == 0) { return MSGTYPE_CONFIRM1; } else if (memcmp(messageTypeString, "Confirm2", 8) == 0) { return MSGTYPE_CONFIRM2; } else if (memcmp(messageTypeString, "Conf2ACK", 8) == 0) { return MSGTYPE_CONF2ACK; } else if (memcmp(messageTypeString, "Error ", 8) == 0) { return MSGTYPE_ERROR; } else if (memcmp(messageTypeString, "ErrorACK", 8) == 0) { return MSGTYPE_ERRORACK; } else if (memcmp(messageTypeString, "GoClear ", 8) == 0) { return MSGTYPE_GOCLEAR; } else if (memcmp(messageTypeString, "ClearACK", 8) == 0) { return MSGTYPE_CLEARACK; } else if (memcmp(messageTypeString, "SASrelay", 8) == 0) { return MSGTYPE_SASRELAY; } else if (memcmp(messageTypeString, "RelayACK", 8) == 0) { return MSGTYPE_RELAYACK; } else if (memcmp(messageTypeString, "Ping ", 8) == 0) { return MSGTYPE_PING; } else if (memcmp(messageTypeString, "PingACK ", 8) == 0) { return MSGTYPE_PINGACK; } else { return MSGTYPE_INVALID; } } /* * @brief Write the message header(preambule, length, message type) into the given output buffer * * @param[out] outputBuffer Message starts at the begining of this buffer * @param[in] messageLength Message length in bytes! To be converted into 32bits words before being inserted in the message header * @param[in] messageType An 8 chars string for the message type (validity is not checked by this function) * */ void zrtpMessageSetHeader(uint8_t *outputBuffer, uint16_t messageLength, uint8_t messageType[8]) { /* insert the preambule */ outputBuffer[0] = 0x50; outputBuffer[1] = 0x5a; /* then the length in 32 bits words (param is in bytes, so >> 2) */ outputBuffer[2] = (uint8_t)((messageLength>>10)&0x00FF); outputBuffer[3] = (uint8_t)((messageLength>>2)&0x00FF); /* the message type */ memcpy(outputBuffer+4, messageType, 8); } /* * Return the variable private value length in bytes according to given key agreement algorythm * * @param[in] keyAgreementAlgo The key agreement algo mapped to an integer as defined in cryptoWrapper.h * * @return the private value length in bytes * */ uint16_t computeKeyAgreementPrivateValueLength(uint8_t keyAgreementAlgo) { uint16_t pvLength = 0; switch (keyAgreementAlgo) { case ZRTP_KEYAGREEMENT_DH3k : pvLength = 384; break; case ZRTP_KEYAGREEMENT_DH2k : pvLength = 256; break; case ZRTP_KEYAGREEMENT_X255 : pvLength = 32; break; case ZRTP_KEYAGREEMENT_X448 : pvLength = 56; break; case ZRTP_KEYAGREEMENT_EC25 : pvLength = 64; break; case ZRTP_KEYAGREEMENT_EC38 : pvLength = 96; break; case ZRTP_KEYAGREEMENT_EC52 : pvLength = 132; break; default : pvLength = 0; break; } return pvLength; } bzrtp-4.4.13/src/pgpwords.c000066400000000000000000000215551364144501400155560ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // https://tools.ietf.org/html/rfc6189 // For the SAS Type of "B256", the most-significant (leftmost) 16 bits // of the 32-bit sasvalue are rendered in network byte order using the // PGP Word List [pgpwordlist] [Juola1][Juola2]. const char * pgpWordsEven[] = { "aardvark", "absurd", "accrue", "acme", "adrift", "adult", "afflict", "ahead", "aimless", "Algol", "allow", "alone", "ammo", "ancient", "apple", "artist", "assume", "Athens", "atlas", "Aztec", "baboon", "backfield", "backward", "banjo", "beaming", "bedlamp", "beehive", "beeswax", "befriend", "Belfast", "berserk", "billiard", "bison", "blackjack", "blockade", "blowtorch", "bluebird", "bombast", "bookshelf", "brackish", "breadline", "breakup", "brickyard", "briefcase", "Burbank", "button", "buzzard", "cement", "chairlift", "chatter", "checkup", "chisel", "choking", "chopper", "Christmas", "clamshell", "classic", "classroom", "cleanup", "clockwork", "cobra", "commence", "concert", "cowbell", "crackdown", "cranky", "crowfoot", "crucial", "crumpled", "crusade", "cubic", "dashboard", "deadbolt", "deckhand", "dogsled", "dragnet", "drainage", "dreadful", "drifter", "dropper", "drumbeat", "drunken", "Dupont", "dwelling", "eating", "edict", "egghead", "eightball", "endorse", "endow", "enlist", "erase", "escape", "exceed", "eyeglass", "eyetooth", "facial", "fallout", "flagpole", "flatfoot", "flytrap", "fracture", "framework", "freedom", "frighten", "gazelle", "Geiger", "glitter", "glucose", "goggles", "goldfish", "gremlin", "guidance", "hamlet", "highchair", "hockey", "indoors", "indulge", "inverse", "involve", "island", "jawbone", "keyboard", "kickoff", "kiwi", "klaxon", "locale", "lockup", "merit", "minnow", "miser", "Mohawk", "mural", "music", "necklace", "Neptune", "newborn", "nightbird", "Oakland", "obtuse", "offload", "optic", "orca", "payday", "peachy", "pheasant", "physique", "playhouse", "Pluto", "preclude", "prefer", "preshrunk", "printer", "prowler", "pupil", "puppy", "python", "quadrant", "quiver", "quota", "ragtime", "ratchet", "rebirth", "reform", "regain", "reindeer", "rematch", "repay", "retouch", "revenge", "reward", "rhythm", "ribcage", "ringbolt", "robust", "rocker", "ruffled", "sailboat", "sawdust", "scallion", "scenic", "scorecard", "Scotland", "seabird", "select", "sentence", "shadow", "shamrock", "showgirl", "skullcap", "skydive", "slingshot", "slowdown", "snapline", "snapshot", "snowcap", "snowslide", "solo", "southward", "soybean", "spaniel", "spearhead", "spellbind", "spheroid", "spigot", "spindle", "spyglass", "stagehand", "stagnate", "stairway", "standard", "stapler", "steamship", "sterling", "stockman", "stopwatch", "stormy", "sugar", "surmount", "suspense", "sweatband", "swelter", "tactics", "talon", "tapeworm", "tempest", "tiger", "tissue", "tonic", "topmost", "tracker", "transit", "trauma", "treadmill", "Trojan", "trouble", "tumor", "tunnel", "tycoon", "uncut", "unearth", "unwind", "uproot", "upset", "upshot", "vapor", "village", "virus", "Vulcan", "waffle", "wallet", "watchword", "wayside", "willow", "woodlark", "Zulu" }; const char * pgpWordsOdd[] = { "adroitness", "adviser", "aftermath", "aggregate", "alkali", "almighty", "amulet", "amusement", "antenna", "applicant", "Apollo", "armistice", "article", "asteroid", "Atlantic", "atmosphere", "autopsy", "Babylon", "backwater", "barbecue", "belowground", "bifocals", "bodyguard", "bookseller", "borderline", "bottomless", "Bradbury", "bravado", "Brazilian", "breakaway", "Burlington", "businessman", "butterfat", "Camelot", "candidate", "cannonball", "Capricorn", "caravan", "caretaker", "celebrate", "cellulose", "certify", "chambermaid", "Cherokee", "Chicago", "clergyman", "coherence", "combustion", "commando", "company", "component", "concurrent", "confidence", "conformist", "congregate", "consensus", "consulting", "corporate", "corrosion", "councilman", "crossover", "crucifix", "cumbersome", "customer", "Dakota", "decadence", "December", "decimal", "designing", "detector", "detergent", "determine", "dictator", "dinosaur", "direction", "disable", "disbelief", "disruptive", "distortion", "document", "embezzle", "enchanting", "enrollment", "enterprise", "equation", "equipment", "escapade", "Eskimo", "everyday", "examine", "existence", "exodus", "fascinate", "filament", "finicky", "forever", "fortitude", "frequency", "gadgetry", "Galveston", "getaway", "glossary", "gossamer", "graduate", "gravity", "guitarist", "hamburger", "Hamilton", "handiwork", "hazardous", "headwaters", "hemisphere", "hesitate", "hideaway", "holiness", "hurricane", "hydraulic", "impartial", "impetus", "inception", "indigo", "inertia", "infancy", "inferno", "informant", "insincere", "insurgent", "integrate", "intention", "inventive", "Istanbul", "Jamaica", "Jupiter", "leprosy", "letterhead", "liberty", "maritime", "matchmaker", "maverick", "Medusa", "megaton", "microscope", "microwave", "midsummer", "millionaire", "miracle", "misnomer", "molasses", "molecule", "Montana", "monument", "mosquito", "narrative", "nebula", "newsletter", "Norwegian", "October", "Ohio", "onlooker", "opulent", "Orlando", "outfielder", "Pacific", "pandemic", "Pandora", "paperweight", "paragon", "paragraph", "paramount", "passenger", "pedigree", "Pegasus", "penetrate", "perceptive", "performance", "pharmacy", "phonetic", "photograph", "pioneer", "pocketful", "politeness", "positive", "potato", "processor", "provincial", "proximate", "puberty", "publisher", "pyramid", "quantity", "racketeer", "rebellion", "recipe", "recover", "repellent", "replica", "reproduce", "resistor", "responsive", "retraction", "retrieval", "retrospect", "revenue", "revival", "revolver", "sandalwood", "sardonic", "Saturday", "savagery", "scavenger", "sensation", "sociable", "souvenir", "specialist", "speculate", "stethoscope", "stupendous", "supportive", "surrender", "suspicious", "sympathy", "tambourine", "telephone", "therapist", "tobacco", "tolerance", "tomorrow", "torpedo", "tradition", "travesty", "trombonist", "truncated", "typewriter", "ultimate", "undaunted", "underfoot", "unicorn", "unify", "universe", "unravel", "upcoming", "vacancy", "vagabond", "vertigo", "Virginia", "visitor", "vocalist", "voyager", "warranty", "Waterloo", "whimsical", "Wichita", "Wilmington", "Wyoming", "yesteryear", "Yucatan" }; bzrtp-4.4.13/src/stateMachine.c000066400000000000000000003371021364144501400163140ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "typedef.h" #include "packetParser.h" #include "cryptoUtils.h" #include "zidCache.h" #include #include "stateMachine.h" /* Local functions prototypes */ int bzrtp_turnIntoResponder(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket, bzrtpCommitMessage_t *commitMessage); int bzrtp_responseToHelloMessage(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket); int bzrtp_computeS0DHMMode(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); int bzrtp_computeS0MultiStreamMode(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); int bzrtp_deriveKeysFromS0(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); int bzrtp_deriveSrtpKeysFromS0(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); int bzrtp_updateCachedSecrets(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext); /* * @brief This is the initial state * On first call, we will create the Hello message and start sending it until we receive an helloACK or a hello message from peer * * Arrives from : * - This is the initial state * Goes to: * - state_discovery_waitingForHello upon HelloACK reception * - state_discovery_waitingForHelloAck upon Hello reception * Send : * - Hello until timer's end or transition */ int state_discovery_init(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; int retval; /*** Manage the first call to this function ***/ /* We are supposed to send Hello packet, it shall be already present int the selfPackets(created at channel init) */ if (event.eventType == BZRTP_EVENT_INIT) { if (zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID] == NULL) { /* We shall never go through this one because Hello packet shall be created at channel init */ int retval; /* create the Hello packet */ bzrtpPacket_t *helloPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_HELLO, &retval); if (retval != 0) { return retval; } /* build the packet string */ if (bzrtp_packetBuild(zrtpContext, zrtpChannelContext, helloPacket, zrtpChannelContext->selfSequenceNumber) ==0) { zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID] = helloPacket; } else { bzrtp_freeZrtpPacket(helloPacket); return retval; } /* TODO: Shall add a warning trace here */ } /* it is the first call to this function, so we must also set the timer for retransmissions */ zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = 0; /* we must send a first hello message as soon as possible, to do it at first timer tick, we can't do it now because still in initialisation phase and the RTP session may not be ready to send a message */ zrtpChannelContext->timer.firingCount = 0; zrtpChannelContext->timer.timerStep = HELLO_BASE_RETRANSMISSION_STEP; zrtpChannelContext->selfSequenceNumber++; return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* now check the type of packet received, we're expecting either Hello or HelloACK */ if ((zrtpPacket->messageType != MSGTYPE_HELLO) && (zrtpPacket->messageType != MSGTYPE_HELLOACK)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* if we have an Hello packet, we must use it to determine which algo we will agree on */ if (zrtpPacket->messageType == MSGTYPE_HELLO) { retval = bzrtp_responseToHelloMessage(zrtpContext, zrtpChannelContext, zrtpPacket); if (retval != 0) { return retval; } /* reset the sending Hello timer as peer may have started slowly and lost all our Hello packets */ zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = 0; zrtpChannelContext->timer.firingCount = 0; zrtpChannelContext->timer.timerStep = HELLO_BASE_RETRANSMISSION_STEP; /* set next state (do not call it as we will just be waiting for a HelloACK packet from peer, nothing to do) */ zrtpChannelContext->stateMachine = state_discovery_waitingForHelloAck; } /* if we have a HelloACK packet, stop the timer and set next state to state_discovery_waitingForHello */ if (zrtpPacket->messageType == MSGTYPE_HELLOACK) { /* stop the timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* Hello ACK packet is not stored, free it */ bzrtp_freeZrtpPacket(zrtpPacket); /* set next state (do not call it as we will just be waiting for a Hello packet from peer, nothing to do) */ zrtpChannelContext->stateMachine = state_discovery_waitingForHello; return 0; } } /*** Manage timer event ***/ if (event.eventType == BZRTP_EVENT_TIMER) { /* adjust timer for next time : check we didn't reach the max retransmissions adjust the step(double it until reaching the cap) */ if (zrtpChannelContext->timer.firingCount<=HELLO_MAX_RETRANSMISSION_NUMBER) { if (2*zrtpChannelContext->timer.timerStep<=HELLO_CAP_RETRANSMISSION_STEP) { zrtpChannelContext->timer.timerStep *= 2; } zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + zrtpChannelContext->timer.timerStep; } else { /* we have done enough retransmissions, stop it */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; } /* We must resend a Hello packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { if (zrtpContext->zrtpCallbacks.bzrtp_sendData!=NULL) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } } else { return retval; } } return 0; } /* * @brief Arrives in this state coming from init upon reception on Hello ACK, we are now waiting for the Hello packet from peer * * Arrives from : * - state_discovery_init upon HelloACK reception * Goes to: * - state_keyAgreement_sendingCommit upon Hello reception * Send : * - HelloACK on Hello reception * */ int state_discovery_waitingForHello(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; /*** Manage the first call to this function ***/ /* no init event for this state */ /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpEvent_t initEvent; int retval; bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* now check the type of packet received, we're expecting either Hello, HelloACK may arrive but will be discarded as useless now */ if (zrtpPacket->messageType != MSGTYPE_HELLO) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; retval = bzrtp_responseToHelloMessage(zrtpContext, zrtpChannelContext, zrtpPacket); if (retval != 0) { return retval; } /* set next state state_keyAgreement_sendingCommit */ zrtpChannelContext->stateMachine = state_keyAgreement_sendingCommit; /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* call the next state */ return zrtpChannelContext->stateMachine(initEvent); } /*** Manage timer event ***/ /* no timer event for this state*/ return 0; } /* * @brief We are now waiting for the HelloACK packet from peer or a Commit packet * * Arrives from : * - state_discovery_init upon Hello reception * Goes to: * - state_keyAgreement_sendingCommit upon HelloACK reception * - state_keyAgreement_responderSendingDHPart1 upon Commit reception in DHM mode * - state_confirmation_responderSendingConfirm1 upon Commit reception in non DHM mode * Send : * - Hello until timer's end or transition * - HelloACK on Hello reception * */ int state_discovery_waitingForHelloAck(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; int retval; /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* we can receive either a Hello, HelloACK or Commit packets, others will be ignored */ if ((zrtpPacket->messageType != MSGTYPE_HELLO) && (zrtpPacket->messageType != MSGTYPE_HELLOACK) && (zrtpPacket->messageType != MSGTYPE_COMMIT)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* We do not need to parse the packet if it is an Hello one as it shall be the duplicate of one we received earlier */ /* we must check it is the same we initially received, and send a HelloACK */ if (zrtpPacket->messageType == MSGTYPE_HELLO) { bzrtpPacket_t *helloACKPacket; if (zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* build and send the HelloACK packet */ helloACKPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_HELLOACK, &retval); if (retval != 0) { return retval; /* no need to free the Hello message as it is attached to the context, it will be freed when destroying it */ } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, helloACKPacket, zrtpChannelContext->selfSequenceNumber); if (retval != 0) { bzrtp_freeZrtpPacket(helloACKPacket); return retval; } else { /* send the message */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, helloACKPacket->packetString, helloACKPacket->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; /* sent HelloACK is not stored, free it */ bzrtp_freeZrtpPacket(helloACKPacket); } return 0; } /* parse the packet wich is either HelloACK or Commit */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* if we have an HelloACK packet, transit to state_keyAgreement_sendingCommit and execute it with an init event */ if (zrtpPacket->messageType == MSGTYPE_HELLOACK) { bzrtpEvent_t initEvent; /* stop the timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* set next state to state_keyAgreement_sendingCommit */ zrtpChannelContext->stateMachine = state_keyAgreement_sendingCommit; /* the HelloACK packet is not stored in context, free it */ bzrtp_freeZrtpPacket(zrtpPacket); /* create the init event for next state and call the next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; return zrtpChannelContext->stateMachine(initEvent); } /* if we have a Commit packet we shall turn into responder role * then transit to state_keyAgreement_responderSendingDHPart1 or state_confirmation_responderSendingConfirm1 depending on which mode (Multi/PreShared or DHM) we are using and execute it with an init event */ if (zrtpPacket->messageType == MSGTYPE_COMMIT) { bzrtpCommitMessage_t *commitMessage = (bzrtpCommitMessage_t *)zrtpPacket->messageData; /* this will stop the timer, update the context channel and run the next state according to current mode */ return bzrtp_turnIntoResponder(zrtpContext, zrtpChannelContext, zrtpPacket, commitMessage); } } /*** Manage timer event ***/ if (event.eventType == BZRTP_EVENT_TIMER) { /* adjust timer for next time : check we didn't reach the max retransmissions adjust the step(double it until reaching the cap) */ if (zrtpChannelContext->timer.firingCount<=HELLO_MAX_RETRANSMISSION_NUMBER) { if (2*zrtpChannelContext->timer.timerStep<=HELLO_CAP_RETRANSMISSION_STEP) { zrtpChannelContext->timer.timerStep *= 2; } zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + zrtpChannelContext->timer.timerStep; } else { /* we have done enough retransmissions, stop it */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; } /* We must resend a Hello packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } else { return retval; } } return 0; } /* * @brief For any kind of key agreement (DHM, Mult, PreShared), we keep sending commit. * * Arrives from : * - state_discovery_waitingForHello upon Hello received * - state_discovery_waitingForHelloAck upon HelloACK received * Goes to: * - state_keyAgreement_initiatorSendingDHPart2 upon DHPart1 reception in DHM mode * - state_confirmation_initiatorSendingConfirm2 upon Confirm1 reception in non DHM mode * - state_keyAgreement_responderSendingDHPart1 upon Commit reception in DHM mode and commit contention gives us the responder role * - state_confirmation_responderSendingConfirm1 upon Commit reception in non DHM mode and commit contention gives us the responder role * Send : * - Commit until timer's end or transition * - HelloACK on Hello reception * */ int state_keyAgreement_sendingCommit(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; int retval; /*** Manage the first call to this function ***/ /* We are supposed to send commit packet, check if we have one in the channel Context, the event type shall be INIT in this case */ if ((event.eventType == BZRTP_EVENT_INIT) && (zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID] == NULL)) { int retval; /* create the commit packet */ bzrtpPacket_t *commitPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_COMMIT, &retval); if (retval != 0) { return retval; } /* build the packet string */ if (bzrtp_packetBuild(zrtpContext, zrtpChannelContext, commitPacket, zrtpChannelContext->selfSequenceNumber) ==0) { zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID] = commitPacket; } else { bzrtp_freeZrtpPacket(commitPacket); return retval; } /* it is the first call to this state function, so we must also set the timer for retransmissions */ zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + NON_HELLO_BASE_RETRANSMISSION_STEP; zrtpChannelContext->timer.firingCount = 0; zrtpChannelContext->timer.timerStep = NON_HELLO_BASE_RETRANSMISSION_STEP; /* now send the first Commit message */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpEvent_t initEvent; bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* now check the type of packet received, we're expecting a commit or a DHPart1 or a Confirm1 packet */ if ((zrtpPacket->messageType != MSGTYPE_COMMIT) && (zrtpPacket->messageType != MSGTYPE_DHPART1) && (zrtpPacket->messageType != MSGTYPE_CONFIRM1)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* DHPART1 can be received only if we are in DHM mode */ if ((zrtpPacket->messageType == MSGTYPE_DHPART1) && ((zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh))) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* Confirm1 can be received only if we are in Mult or PreShared mode */ if ((zrtpPacket->messageType == MSGTYPE_CONFIRM1) && (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Prsh) && (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* if we have a confirm1 and are in multi stream mode, we must first derive s0 and other keys to be able to parse the packet */ if ((zrtpPacket->messageType == MSGTYPE_CONFIRM1) && (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { retval = bzrtp_computeS0MultiStreamMode(zrtpContext, zrtpChannelContext); if (retval!= 0) { return retval; } } /* parse the packet wich is either Commit a DHPart1 or a Confirm1 packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* we have a DHPart1 - so we are initiator in DHM mode - stop timer and go to state_keyAgreement_initiatorSendingDHPart2 */ if(zrtpPacket->messageType == MSGTYPE_DHPART1) { bzrtpDHPartMessage_t * dhPart1Message; /* stop the timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; dhPart1Message = (bzrtpDHPartMessage_t *)zrtpPacket->messageData; /* Check shared secret hash found in the DHPart1 message */ /* if we do not have the secret, don't check it as we do not expect the other part to have it neither */ /* check matching secret is: check if locally computed(by the initiator) rs1 matches the responder rs1 or rs2 */ /* if it doesn't check with initiator's rs2 and responder(received in DHPart1) rs1 or rs2 */ /* In case of cache mismatch, warn the user(reset the Previously Verified Sas Flag) and erase secret as it must not be used to compute s0 */ if (zrtpContext->cachedSecret.rs1!=NULL) { /* check if rs1 match peer's one */ if (memcmp(zrtpContext->responderCachedSecretHash.rs1ID, dhPart1Message->rs1ID,8) != 0) { /* they don't match but self rs1 may match peer rs2 */ if (memcmp(zrtpContext->responderCachedSecretHash.rs1ID, dhPart1Message->rs2ID,8) != 0) { /* They don't match either : erase rs1 and set the cache mismatch flag(which may be unset if self rs2 match peer rs1 or peer rs2 */ free(zrtpContext->cachedSecret.rs1); zrtpContext->cachedSecret.rs1= NULL; zrtpContext->cachedSecret.rs1Length = 0; zrtpContext->cacheMismatchFlag = 1; /* Do not trigger cache mismatch message for now as it may match rs2 */ } } } /* we didn't have a match with rs1 and have a rs2; check if it matches responder rs1 or rs2 */ if ((zrtpContext->cacheMismatchFlag == 1) && (zrtpContext->cachedSecret.rs2!=NULL)) { /* does it match rs1 */ if (memcmp(zrtpContext->responderCachedSecretHash.rs2ID, dhPart1Message->rs1ID,8) != 0) { /* it doesn't match rs1 but may match rs2 */ if (memcmp(zrtpContext->responderCachedSecretHash.rs2ID, dhPart1Message->rs2ID,8) != 0) { free(zrtpContext->cachedSecret.rs2); zrtpContext->cachedSecret.rs2= NULL; zrtpContext->cachedSecret.rs2Length = 0; } else { /* it matches rs2, reset the cache mismatch flag */ zrtpContext->cacheMismatchFlag = 0; } } else { /* it matches rs1, reset the cache mismatch flag */ zrtpContext->cacheMismatchFlag = 0; } } /* if we have an aux secret check it match peer's one */ if (zrtpContext->cachedSecret.auxsecret!=NULL) { if (memcmp(zrtpChannelContext->responderAuxsecretID, dhPart1Message->auxsecretID,8) != 0) { // they do not match, set flag to MISMATCH, delete the aux secret as we must not use it free(zrtpContext->cachedSecret.auxsecret); zrtpContext->cachedSecret.auxsecret= NULL; zrtpContext->cachedSecret.auxsecretLength = 0; zrtpChannelContext->srtpSecrets.auxSecretMismatch = BZRTP_AUXSECRET_MISMATCH; } else { // they do match, set the flag to MATCH (default is UNSET) zrtpChannelContext->srtpSecrets.auxSecretMismatch = BZRTP_AUXSECRET_MATCH; } } if (zrtpContext->cachedSecret.pbxsecret!=NULL) { if (memcmp(zrtpContext->responderCachedSecretHash.pbxsecretID, dhPart1Message->pbxsecretID,8) != 0) { free(zrtpContext->cachedSecret.pbxsecret); zrtpContext->cachedSecret.pbxsecret= NULL; zrtpContext->cachedSecret.pbxsecretLength = 0; } } /* in case of cache mismatch, be sure the Previously Verified Sas flag is reset in cache and in the context */ if (zrtpContext->cacheMismatchFlag == 1) { uint8_t pvsFlag = 0; const char *colNames[] = {"pvs"}; uint8_t *colValues[] = {&pvsFlag}; size_t colLength[] = {1}; zrtpContext->cachedSecret.previouslyVerifiedSas = 0; bzrtp_cache_write_active(zrtpContext, "zrtp", colNames, colValues, colLength, 1); /* if we have a statusMessage callback, use it to warn user */ if (zrtpContext->zrtpCallbacks.bzrtp_statusMessage!=NULL && zrtpContext->zrtpCallbacks.bzrtp_messageLevel>=BZRTP_MESSAGE_ERROR) { /* use error level as this one MUST (RFC section 4.3.2) be warned */ zrtpContext->zrtpCallbacks.bzrtp_statusMessage(zrtpChannelContext->clientData, BZRTP_MESSAGE_ERROR, BZRTP_MESSAGE_CACHEMISMATCH, NULL); } } /* Check that the received PV is not 1 or Prime-1 TODO*/ /* update context with the information found in the packet */ memcpy(zrtpChannelContext->peerH[1], dhPart1Message->H1, 32); zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID] = zrtpPacket; /* Compute the shared DH secret */ if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(zrtpContext->keyAgreementContext); DHMContext->peer = (uint8_t *)malloc(zrtpChannelContext->keyAgreementLength*sizeof(uint8_t)); memcpy (DHMContext->peer, dhPart1Message->pv, zrtpChannelContext->keyAgreementLength); bctbx_DHMComputeSecret(DHMContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)zrtpContext->RNGContext); } if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(zrtpContext->keyAgreementContext); ECDHContext->peerPublic = (uint8_t *)malloc(zrtpChannelContext->keyAgreementLength*sizeof(uint8_t)); memcpy (ECDHContext->peerPublic, dhPart1Message->pv, zrtpChannelContext->keyAgreementLength); bctbx_ECDHComputeSecret(ECDHContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)zrtpContext->RNGContext); } /* Derive the s0 key */ bzrtp_computeS0DHMMode(zrtpContext, zrtpChannelContext); /* set next state to state_keyAgreement_initiatorSendingDHPart2 */ zrtpChannelContext->stateMachine = state_keyAgreement_initiatorSendingDHPart2; /* call it with the init event */ return zrtpChannelContext->stateMachine(initEvent); } /* we have a Confirm1 - so we are initiator and in NON-DHM mode - stop timer and go to state_confirmation_initiatorSendingConfirm2 */ if(zrtpPacket->messageType == MSGTYPE_CONFIRM1) { bzrtpConfirmMessage_t *confirm1Message; /* stop the timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* save the message and extract some information from it to the channel context */ confirm1Message = (bzrtpConfirmMessage_t *)zrtpPacket->messageData; memcpy(zrtpChannelContext->peerH[0], confirm1Message->H0, 32); /* store the packet to check possible repetitions */ zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID] = zrtpPacket; /* set next state to state_confirmation_responderSendingConfirm2 */ zrtpChannelContext->stateMachine = state_confirmation_initiatorSendingConfirm2; /* call it with the init event */ return zrtpChannelContext->stateMachine(initEvent); } /* we have a commit - do commit contention as in rfc section 4.2 - if we are initiator, keep sending Commits, otherwise stop the timer and go to state_keyAgreement_responderSendingDHPart1 if we are DHM mode or state_confirmation_responderSendingConfirm1 in Multi or PreShared mode */ if(zrtpPacket->messageType == MSGTYPE_COMMIT) { bzrtpCommitMessage_t *peerCommitMessage = (bzrtpCommitMessage_t *)zrtpPacket->messageData; bzrtpCommitMessage_t *selfCommitMessage = (bzrtpCommitMessage_t *)zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageData; /* - If one Commit is for a DH mode while the other is for Preshared mode, then the Preshared Commit MUST be discarded and the DH Commit proceeds * * - If the two Commits are both Preshared mode, and one party has set the MiTM (M) flag in the Hello message and the other has not, the Commit message from the party who set the (M) flag MUST be discarded, and the one who has not set the (M) flag becomes the initiator, regardless of the nonce values. In other words, for Preshared mode, the phone is the initiator and the PBX is the responder. * * - If the two Commits are either both DH modes or both non-DH modes, then the Commit message with the lowest hvi (hash value of initiator) value (for DH Commits), or lowest nonce value (for non-DH Commits), MUST be discarded and the other side is the initiator, and the protocol proceeds with the initiator's Commit. The two hvi or nonce values are compared as large unsigned integers in network byte order. */ /* we are by default initiator, so just check the statement which turns us into responder */ if (peerCommitMessage->keyAgreementAlgo != selfCommitMessage->keyAgreementAlgo ) { /* commits have differents modes */ if ((peerCommitMessage->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Prsh) && (selfCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh)) { zrtpChannelContext->role = BZRTP_ROLE_RESPONDER; } } else { /* commit have the same mode */ bzrtpHelloMessage_t *peerHelloMessage = (bzrtpHelloMessage_t *)zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData; bzrtpHelloMessage_t *selfHelloMessage = (bzrtpHelloMessage_t *)zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageData; if (peerCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh && ((selfHelloMessage->M == 1) || (peerHelloMessage->M == 1)) ) { if (selfHelloMessage->M == 1) { /* we are a PBX -> act as responder */ zrtpChannelContext->role = BZRTP_ROLE_RESPONDER; } } else { /* modes are the same and no one has the MiTM flag set : compare hvi/nonce */ if ((selfCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (selfCommitMessage->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { /* non DHM mode, compare the nonce, lower will be responder */ if (memcmp(selfCommitMessage->nonce, peerCommitMessage->nonce, 16) < 0) { /* self nonce < peer nonce */ zrtpChannelContext->role = BZRTP_ROLE_RESPONDER; } } else { /* DHM mode, compare the hvi */ if (memcmp(selfCommitMessage->hvi, peerCommitMessage->hvi, 32) < 0) { /* self hvi < peer hvi */ zrtpChannelContext->role = BZRTP_ROLE_RESPONDER; } } } } /* so now check if we are responder - if we are initiator just do nothing, continue sending the commits and ignore the one we just receive */ if (zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { /* free the self commit packet as it is now useless */ bzrtp_freeZrtpPacket(zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]); zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID] = NULL; /* this will update the context channel and run the next state according to current mode */ return bzrtp_turnIntoResponder(zrtpContext, zrtpChannelContext, zrtpPacket, peerCommitMessage); } else { /* we are iniator, just drop the incoming commit packet and keep send our */ bzrtp_freeZrtpPacket(zrtpPacket); } } return 0; } /*** Manage timer event ***/ if (event.eventType == BZRTP_EVENT_TIMER) { /* adjust timer for next time : check we didn't reach the max retransmissions adjust the step(double it until reaching the cap) */ if (zrtpChannelContext->timer.firingCount<=NON_HELLO_MAX_RETRANSMISSION_NUMBER) { if (2*zrtpChannelContext->timer.timerStep<=NON_HELLO_CAP_RETRANSMISSION_STEP) { zrtpChannelContext->timer.timerStep *= 2; } zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + zrtpChannelContext->timer.timerStep; } else { /* we have done enough retransmissions, stop it */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; } /* We must resend a Commit packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } else { return retval; } } return 0; } /* * @brief For DHM mode only, responder send DHPart1 packet * * Arrives from: * - state_discovery_waitingForHelloAck upon Commit reception in DHM mode * - state_keyAgreement_sendingCommit upon Commit reception in DHM mode and commit contention gives us the responder role * Goes to: * - state_confirmation_responderSendingConfirm1 upon DHPart2 reception * Send : * - DHPart1 on Commit reception * */ int state_keyAgreement_responderSendingDHPart1(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; int retval; /* We are supposed to send DHPart1 packet and it is already in context(built from DHPart when turning into responder) or it's an error */ if (zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID] == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } /*** Manage the first call to this function ***/ if (event.eventType == BZRTP_EVENT_INIT) { /* There is no timer in this state, make sure it is off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* now send the first DHPart1 message, note the sequence number has already been incremented when turning the DHPart2 message in DHPart1 */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* now check the type of packet received, we're expecting DHPart2 or a Commit packet */ if ((zrtpPacket->messageType != MSGTYPE_COMMIT) && (zrtpPacket->messageType != MSGTYPE_DHPART2)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* we have a Commit, check it is the same as received previously and resend the DHPart1 packet */ if(zrtpPacket->messageType == MSGTYPE_COMMIT) { if (zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* update and send the DHPart1 packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval != 0) { return retval; } zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; return 0; } /* we have a DHPart2 go to state_confirmation_responderSendingConfirm1 */ if(zrtpPacket->messageType == MSGTYPE_DHPART2) { bzrtpDHPartMessage_t * dhPart2Message; uint8_t cacheMatchFlag = 0; bzrtpEvent_t initEvent; /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } dhPart2Message = (bzrtpDHPartMessage_t *)zrtpPacket->messageData; /* Check shared secret hash found in the DHPart2 message */ /* if we do not have the secret, don't check it as we do not expect the other part to have it neither */ /* shared secret matching is: check if initiator rs1(received in DHPart2) match localy computed(by responder from cache) rs1 or rs2 */ /* of not check if received rs2 match local rs1 or rs2 */ /* keep the rs1 or rs2 in cachedSecret only if it matches the received one, it will the be used to compute s0 */ /* In case of cache mismatch, warn the user(reset the previously verified Sas flag) and erase secret as it must not be used to compute s0 */ if (zrtpContext->cachedSecret.rs1!=NULL) { /* check if rs1 match peer's one */ if (memcmp(zrtpContext->initiatorCachedSecretHash.rs1ID, dhPart2Message->rs1ID,8) != 0) { /* they don't match but self rs2 may match peer rs1 */ if (zrtpContext->cachedSecret.rs2!=NULL) { if (memcmp(zrtpContext->initiatorCachedSecretHash.rs2ID, dhPart2Message->rs1ID,8) == 0) { /* responder rs2 match initiator rs1, erase responder rs1 */ cacheMatchFlag = 1; free(zrtpContext->cachedSecret.rs1); zrtpContext->cachedSecret.rs1= NULL; zrtpContext->cachedSecret.rs1Length = 0; } } } else { /* responder rs1 match initiator rs1 */ cacheMatchFlag = 1; } } /* if we didn't found a match yet(initiator rs1 doesn't match local rs1 or rs2) */ if ((zrtpContext->cachedSecret.rs1!=NULL) && (cacheMatchFlag == 0)) { /* does it match initiator rs2 */ if (memcmp(zrtpContext->initiatorCachedSecretHash.rs1ID, dhPart2Message->rs2ID,8) != 0) { /* it doesn't match rs1 (erase it) but may match rs2 */ free(zrtpContext->cachedSecret.rs1); zrtpContext->cachedSecret.rs1= NULL; zrtpContext->cachedSecret.rs1Length = 0; if (zrtpContext->cachedSecret.rs2!=NULL) { if (memcmp(zrtpContext->initiatorCachedSecretHash.rs2ID, dhPart2Message->rs2ID,8) != 0) { /* no match found erase rs2 and set the cache mismatch flag */ free(zrtpContext->cachedSecret.rs2); zrtpContext->cachedSecret.rs2= NULL; zrtpContext->cachedSecret.rs2Length = 0; zrtpContext->cacheMismatchFlag = 1; } } else { zrtpContext->cacheMismatchFlag = 1; } } } /* if we have an auxiliary secret, check it match peer's one */ if (zrtpContext->cachedSecret.auxsecret!=NULL) { if (memcmp(zrtpChannelContext->initiatorAuxsecretID, dhPart2Message->auxsecretID,8) != 0) { // they do not match, set flag to MISMATCH, delete the aux secret as we must not use it free(zrtpContext->cachedSecret.auxsecret); zrtpContext->cachedSecret.auxsecret= NULL; zrtpContext->cachedSecret.auxsecretLength = 0; zrtpChannelContext->srtpSecrets.auxSecretMismatch = BZRTP_AUXSECRET_MISMATCH; } else { // they do match, set the flag to MATCH (default is UNSET) zrtpChannelContext->srtpSecrets.auxSecretMismatch = BZRTP_AUXSECRET_MATCH; } } if (zrtpContext->cachedSecret.pbxsecret!=NULL) { if (memcmp(zrtpContext->initiatorCachedSecretHash.pbxsecretID, dhPart2Message->pbxsecretID,8) != 0) { free(zrtpContext->cachedSecret.pbxsecret); zrtpContext->cachedSecret.pbxsecret= NULL; zrtpContext->cachedSecret.pbxsecretLength = 0; } } /* in case of cache mismatch, be sure the Previously Verified Sas flag is reset in cache and in the context */ if (zrtpContext->cacheMismatchFlag == 1) { uint8_t pvsFlag = 0; const char *colNames[] = {"pvs"}; uint8_t *colValues[] = {&pvsFlag}; size_t colLength[] = {1}; zrtpContext->cachedSecret.previouslyVerifiedSas = 0; bzrtp_cache_write_active(zrtpContext, "zrtp", colNames, colValues, colLength, 1); /* if we have a statusMessage callback, use it to warn user */ if (zrtpContext->zrtpCallbacks.bzrtp_statusMessage!=NULL && zrtpContext->zrtpCallbacks.bzrtp_messageLevel>=BZRTP_MESSAGE_ERROR) { /* use error level as this one MUST (RFC section 4.3.2) be warned */ zrtpContext->zrtpCallbacks.bzrtp_statusMessage(zrtpChannelContext->clientData, BZRTP_MESSAGE_ERROR, BZRTP_MESSAGE_CACHEMISMATCH, NULL); } } /* Check that the received PV is not 1 or Prime-1 : is performed by crypto lib */ /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* update context with the information found in the packet */ memcpy(zrtpChannelContext->peerH[1], dhPart2Message->H1, 32); zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID] = zrtpPacket; /* Compute the shared DH secret */ if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(zrtpContext->keyAgreementContext); DHMContext->peer = (uint8_t *)malloc(zrtpChannelContext->keyAgreementLength*sizeof(uint8_t)); memcpy (DHMContext->peer, dhPart2Message->pv, zrtpChannelContext->keyAgreementLength); bctbx_DHMComputeSecret(DHMContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)zrtpContext->RNGContext); } if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(zrtpContext->keyAgreementContext); ECDHContext->peerPublic = (uint8_t *)malloc(zrtpChannelContext->keyAgreementLength*sizeof(uint8_t)); memcpy (ECDHContext->peerPublic, dhPart2Message->pv, zrtpChannelContext->keyAgreementLength); bctbx_ECDHComputeSecret(ECDHContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)zrtpContext->RNGContext); } /* Derive the s0 key */ bzrtp_computeS0DHMMode(zrtpContext, zrtpChannelContext); /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* set the next state to state_confirmation_responderSendingConfirm1 */ zrtpChannelContext->stateMachine = state_confirmation_responderSendingConfirm1; /* call it with the init event */ return zrtpChannelContext->stateMachine(initEvent); } } /*** Manage timer event ***/ /* no timer for this state, initiator only retransmit packets, we just send DHPart1 when ever a commit arrives */ return 0; } /* * @brief For DHM mode only, initiator send DHPart2 packet * * Arrives from: * - state_keyAgreement_sendingCommit upon DHPart1 reception * Goes to: * - state_confirmation_initiatorSendingConfirm2 upon reception of Confirm1 * Send : * - DHPart2 until timer's end or transition * */ int state_keyAgreement_initiatorSendingDHPart2(bzrtpEvent_t event) { int retval; /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; /*** Manage the first call to this function ***/ /* We have to send a DHPart2 packet, it is already present in the context */ if (event.eventType == BZRTP_EVENT_INIT) { /* adjust the sequence number and sennd the packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } else { return retval; } /* it is the first call to this state function, so we must set the timer for retransmissions */ zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + NON_HELLO_BASE_RETRANSMISSION_STEP; zrtpChannelContext->timer.firingCount = 0; zrtpChannelContext->timer.timerStep = NON_HELLO_BASE_RETRANSMISSION_STEP; return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* now check the type of packet received, we're expecting a confirm1 packet, DHPart1 packet may arrives, just check they are the same we previously received */ if ((zrtpPacket->messageType != MSGTYPE_DHPART1) && (zrtpPacket->messageType != MSGTYPE_CONFIRM1)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* we have DHPart1 packet, just check it is the same we received previously and do nothing */ if (zrtpPacket->messageType == MSGTYPE_DHPART1) { if (zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); return 0; } /* we have a confirm1 packet, go to state_confirmation_initiatorSendingConfirm2 state */ if (zrtpPacket->messageType == MSGTYPE_CONFIRM1) { bzrtpConfirmMessage_t *confirm1Packet; bzrtpEvent_t initEvent; /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* stop the timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* update context with the information found in the packet */ confirm1Packet = (bzrtpConfirmMessage_t *)zrtpPacket->messageData; memcpy(zrtpChannelContext->peerH[0], confirm1Packet->H0, 32); /* on the first channel, set peerPVS in context */ if (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult) { zrtpContext->peerPVS=confirm1Packet->V; } /* store the packet to check possible repetitions */ zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID] = zrtpPacket; /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* next state is state_confirmation_initiatorSendingConfirm2*/ zrtpChannelContext->stateMachine = state_confirmation_initiatorSendingConfirm2; /* call the next state with the init event */ return zrtpChannelContext->stateMachine(initEvent); } } /*** Manage timer event ***/ if (event.eventType == BZRTP_EVENT_TIMER) { /* adjust timer for next time : check we didn't reach the max retransmissions adjust the step(double it until reaching the cap) */ if (zrtpChannelContext->timer.firingCount<=NON_HELLO_MAX_RETRANSMISSION_NUMBER) { if (2*zrtpChannelContext->timer.timerStep<=NON_HELLO_CAP_RETRANSMISSION_STEP) { zrtpChannelContext->timer.timerStep *= 2; } zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + zrtpChannelContext->timer.timerStep; } else { /* we have done enough retransmissions, stop it */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; } /* We must resend a DHPart1 packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } else { return retval; } } return 0; } /* * @brief Responder send the confirm1 message * * Arrives from: * - state_keyAgreement_responderSendingDHPart1 upon DHPart2 reception * - state_keyAgreement_sendingCommit upon Commit reception in non DHM mode and commit contention gives us the responder role * - state_discovery_waitingForHelloAck upon Commit reception in non DHM mode * Goes to: * - state_secure on Confirm2 reception * Send : * - Confirm1 on Commit or DHPart2 reception * */ int state_confirmation_responderSendingConfirm1(bzrtpEvent_t event) { int retval; /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; /*** Manage the first call to this function ***/ if (event.eventType == BZRTP_EVENT_INIT) { bzrtpPacket_t *confirm1Packet; /* when in multistream mode, we must derive s0 and other keys from ZRTPSess */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult) { /* we need ZRTPSess */ if (zrtpContext->ZRTPSess == NULL) { return BZRTP_ERROR_INVALIDCONTEXT; } retval = bzrtp_computeS0MultiStreamMode(zrtpContext, zrtpChannelContext); if (retval!= 0) { return retval; } } else if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { /* when in preShared mode: todo */ } else { /* we are in DHM mode, check that we have the keys needed to build the confirm1 packet */ /* we must build the confirm1 packet, check in the channel context if we have the needed keys */ if ((zrtpChannelContext->mackeyr == NULL) || (zrtpChannelContext->zrtpkeyr == NULL)) { return BZRTP_ERROR_INVALIDCONTEXT; } } /* There is no timer in this state, make sure it is off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* build the confirm1 packet */ confirm1Packet = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_CONFIRM1, &retval); if (retval!=0) { return retval; } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, confirm1Packet, zrtpChannelContext->selfSequenceNumber); if (retval!=0) { bzrtp_freeZrtpPacket(confirm1Packet); return retval; } zrtpChannelContext->selfSequenceNumber++; /* save it so we can send it again if needed */ zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID] = confirm1Packet; /* now send the confirm1 message */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* we expect packets Confirm2, Commit in NON-DHM mode or DHPart2 in DHM mode */ if ((zrtpPacket->messageType != MSGTYPE_CONFIRM2) && (zrtpPacket->messageType != MSGTYPE_COMMIT) && (zrtpPacket->messageType != MSGTYPE_DHPART2)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* We have a commit packet, check we are in non DHM mode, that the commit is identical to the one we already had and resend the Confirm1 packet */ if (zrtpPacket->messageType == MSGTYPE_COMMIT) { /* Check we are not in DHM mode */ if ((zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Prsh) && (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* Check the commit packet is the same we already had */ if (zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* update sequence number and resend confirm1 packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval!=0) { return retval; } zrtpChannelContext->selfSequenceNumber++; return zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); } /* We have a DHPart2 packet, check we are in DHM mode, that the DHPart2 is identical to the one we already had and resend the Confirm1 packet */ if (zrtpPacket->messageType == MSGTYPE_DHPART2) { /* Check we are not DHM mode */ if ((zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* Check the DHPart2 packet is the same we already had */ if (zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* update sequence number and resend confirm1 packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval!=0) { return retval; } zrtpChannelContext->selfSequenceNumber++; return zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); } /* We have a Confirm2 packet, check it, send a conf2ACK and go to secure state */ if (zrtpPacket->messageType == MSGTYPE_CONFIRM2) { bzrtpConfirmMessage_t *confirm2Packet; bzrtpPacket_t *conf2ACKPacket; bzrtpEvent_t initEvent; /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* update context with the information found in the packet */ confirm2Packet = (bzrtpConfirmMessage_t *)zrtpPacket->messageData; memcpy(zrtpChannelContext->peerH[0], confirm2Packet->H0, 32); /* on the first channel, set peerPVS in context */ if (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult) { zrtpContext->peerPVS = confirm2Packet->V; } /* store the packet to check possible repetitions : note the storage points to confirm1, delete it as we don't need it anymore */ bzrtp_freeZrtpPacket(zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]); zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID] = zrtpPacket; /* packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* compute SAS and srtp secrets */ retval = bzrtp_deriveSrtpKeysFromS0(zrtpContext, zrtpChannelContext); if (retval!=0) { return retval; } /* compute and update in cache the retained shared secret */ bzrtp_updateCachedSecrets(zrtpContext, zrtpChannelContext); /* send them to the environment for receiver as we may receive a srtp packet in response to our conf2ACK */ if (zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable != NULL) { zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable(zrtpChannelContext->clientData, &zrtpChannelContext->srtpSecrets, ZRTP_SRTP_SECRETS_FOR_RECEIVER); } /* create and send a conf2ACK packet */ conf2ACKPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_CONF2ACK, &retval); if (retval!=0) { return retval; } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, conf2ACKPacket, zrtpChannelContext->selfSequenceNumber); if (retval!=0) { bzrtp_freeZrtpPacket(conf2ACKPacket); return retval; } zrtpChannelContext->selfSequenceNumber++; retval = zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, conf2ACKPacket->packetString, conf2ACKPacket->messageLength+ZRTP_PACKET_OVERHEAD); bzrtp_freeZrtpPacket(conf2ACKPacket); if (retval!=0) { return retval; } /* send SRTP secrets for sender as we can start sending srtp packets just after sending the conf2ACK */ if (zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable != NULL) { zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable(zrtpChannelContext->clientData, &zrtpChannelContext->srtpSecrets, ZRTP_SRTP_SECRETS_FOR_SENDER); } /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* next state is state_secure */ zrtpChannelContext->stateMachine = state_secure; /* call the next state with the init event */ return zrtpChannelContext->stateMachine(initEvent); } } /*** Manage timer event ***/ /* no timer for this state, initiator only retransmit packets, we just send confirm11 when ever a DHPart2 or commit arrives */ return 0; } /* * @brief Initiator send the confirm2 message * * Arrives from: * - state_keyAgreement_initiatorSendingDHPart2 upon confirm1 reception * - state_keyAgreement_sendingCommit upon Confirm1 reception in non DHM mode * Goes to: * - state_secure on Conf2ACK reception or first SRTP message * Send : * - Confirm2 until timer's end or transition * */ int state_confirmation_initiatorSendingConfirm2(bzrtpEvent_t event) { int retval; /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; /*** Manage the first call to this function ***/ if (event.eventType == BZRTP_EVENT_INIT) { bzrtpPacket_t *confirm2Packet; /* we must build the confirm2 packet, check in the channel context if we have the needed keys */ if ((zrtpChannelContext->mackeyi == NULL) || (zrtpChannelContext->zrtpkeyi == NULL)) { return BZRTP_ERROR_INVALIDCONTEXT; } /* build the confirm2 packet */ confirm2Packet = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_CONFIRM2, &retval); if (retval!=0) { return retval; } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, confirm2Packet, zrtpChannelContext->selfSequenceNumber); if (retval!=0) { bzrtp_freeZrtpPacket(confirm2Packet); return retval; } /* save it so we can send it again if needed */ zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID] = confirm2Packet; /* compute SAS and SRTP secrets as responder may directly send SRTP packets and non conf2ACK */ retval = bzrtp_deriveSrtpKeysFromS0(zrtpContext, zrtpChannelContext); if (retval!=0) { return retval; } /* send them to the environment, but specify they are for receiver only as we must wait for the conf2ACK or the first valid srtp packet from peer * to start sending srtp packets */ if (zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable != NULL) { zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable(zrtpChannelContext->clientData, &zrtpChannelContext->srtpSecrets, ZRTP_SRTP_SECRETS_FOR_RECEIVER); } /* now send the confirm2 message */ retval = zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); if (retval != 0) { return retval; } zrtpChannelContext->selfSequenceNumber++; /* it is the first call to this state function, so we must set the timer for retransmissions */ zrtpChannelContext->timer.status = BZRTP_TIMER_ON; zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + NON_HELLO_BASE_RETRANSMISSION_STEP; zrtpChannelContext->timer.firingCount = 0; zrtpChannelContext->timer.timerStep = NON_HELLO_BASE_RETRANSMISSION_STEP; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* we expect packets Conf2ACK, or a Confirm1 */ if ((zrtpPacket->messageType != MSGTYPE_CONFIRM1) && (zrtpPacket->messageType != MSGTYPE_CONF2ACK)) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* we have confirm1 packet, just check it is the same we received previously and do nothing */ if (zrtpPacket->messageType == MSGTYPE_CONFIRM1) { if (zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); return 0; } /* we have a conf2ACK packet, parse it to validate it, stop the timer and go to secure state */ if (zrtpPacket->messageType == MSGTYPE_CONF2ACK) { bzrtpEvent_t initEvent; /* parse the packet */ retval = bzrtp_packetParser(zrtpContext, zrtpChannelContext, event.bzrtpPacketString, event.bzrtpPacketStringLength, zrtpPacket); if (retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* stop the retransmission timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* compute and update in cache the retained shared secret */ bzrtp_updateCachedSecrets(zrtpContext, zrtpChannelContext); /* send the sender srtp keys to the client(we sent receiver only when the first confirm1 packet arrived) */ if (zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable != NULL) { zrtpContext->zrtpCallbacks.bzrtp_srtpSecretsAvailable(zrtpChannelContext->clientData, &zrtpChannelContext->srtpSecrets, ZRTP_SRTP_SECRETS_FOR_SENDER); } /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* next state is state_secure */ zrtpChannelContext->stateMachine = state_secure; /* call the next state with the init event */ return zrtpChannelContext->stateMachine(initEvent); } } /*** Manage timer event ***/ if (event.eventType == BZRTP_EVENT_TIMER) { /* adjust timer for next time : check we didn't reach the max retransmissions adjust the step(double it until reaching the cap) */ if (zrtpChannelContext->timer.firingCount<=NON_HELLO_MAX_RETRANSMISSION_NUMBER) { if (2*zrtpChannelContext->timer.timerStep<=NON_HELLO_CAP_RETRANSMISSION_STEP) { zrtpChannelContext->timer.timerStep *= 2; } zrtpChannelContext->timer.firingTime = zrtpContext->timeReference + zrtpChannelContext->timer.timerStep; } else { /* we have done enough retransmissions, stop it */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; } /* We must resend a Confirm2 packet */ retval = bzrtp_packetUpdateSequenceNumber(zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->packetString, zrtpChannelContext->selfPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; } else { return retval; } } return 0; } /* * @brief We are in secure state * * Arrives from: * - state_confirmation_responderSendingConfirm1 on Confirm2 reception * - state_confirmation_initiatorSendingConfirm2 on conf2ACK or first SRTP message * Goes to: * - This is the end(we do not support GoClear message), state machine may be destroyed after going to secure mode * Send : * - Conf2ACK on Confirm2 reception * */ int state_secure(bzrtpEvent_t event) { /* get the contextes from the event */ bzrtpContext_t *zrtpContext = event.zrtpContext; bzrtpChannelContext_t *zrtpChannelContext = event.zrtpChannelContext; /*** Manage the first call to this function ***/ if (event.eventType == BZRTP_EVENT_INIT) { /* there is no timer in this state, turn it off */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* if we are not in Multistream mode, turn the global context isSecure flag to 1 */ if (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult) { zrtpContext->isSecure = 1; } /* turn channel isSecure flag to 1 */ zrtpChannelContext->isSecure = 1; /* call the environment to signal we're ready to operate */ if (zrtpContext->zrtpCallbacks.bzrtp_startSrtpSession!= NULL) { zrtpContext->zrtpCallbacks.bzrtp_startSrtpSession(zrtpChannelContext->clientData, &(zrtpChannelContext->srtpSecrets), zrtpContext->cachedSecret.previouslyVerifiedSas && zrtpContext->peerPVS); } return 0; } /*** Manage message event ***/ if (event.eventType == BZRTP_EVENT_MESSAGE) { int retval; bzrtpPacket_t *conf2ACKPacket; bzrtpPacket_t *zrtpPacket = event.bzrtpPacket; /* we expect confirm2 packet */ if (zrtpPacket->messageType != MSGTYPE_CONFIRM2) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE; } /* We have a confirm2 packet, check it is identical to the one we already had and resend the Conf2ACK packet */ /* Check the commit packet is the same we already had */ if (zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength != zrtpPacket->messageLength) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } if (memcmp(event.bzrtpPacketString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[CONFIRM_MESSAGE_STORE_ID]->messageLength) != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNMATCHINGPACKETREPETITION; } /* incoming packet is valid, set the sequence Number in channel context */ zrtpChannelContext->peerSequenceNumber = zrtpPacket->sequenceNumber; /* free the incoming packet */ bzrtp_freeZrtpPacket(zrtpPacket); /* create and send a conf2ACK packet */ conf2ACKPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_CONF2ACK, &retval); if (retval!=0) { return retval; } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, conf2ACKPacket, zrtpChannelContext->selfSequenceNumber); if (retval!=0) { bzrtp_freeZrtpPacket(conf2ACKPacket); return retval; } zrtpChannelContext->selfSequenceNumber++; retval = zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, conf2ACKPacket->packetString, conf2ACKPacket->messageLength+ZRTP_PACKET_OVERHEAD); /* free the conf2ACK packet */ bzrtp_freeZrtpPacket(conf2ACKPacket); return retval; } /*** Manage timer event ***/ /* no timer in this state */ return 0; } /** * @brief Turn the current Channel into responder role * This happens when receiving a commit message when in state state_discovery_waitingForHelloAck or state_keyAgreement_sendingCommit if commit contention gives us the responder role. * State will be changed to state_confirmation_responderSendingConfirm1 or state_confirmation_responderSendingDHPart1 depending on DHM or non-DHM operation mode * * @param[in] zrtpContext The current zrtp Context * @param[in/out] zrtpChannelContext The channel we are operating * @param[in] zrtpPacket The zrtpPacket receives, it contains the commit message * @param[in] commitMessage A direct pointer to the commitMessage structure contained in the zrtp packet * * @return 0 on succes, error code otherwise */ int bzrtp_turnIntoResponder(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket, bzrtpCommitMessage_t *commitMessage) { bzrtpEvent_t initEvent; /* kill the ongoing timer */ zrtpChannelContext->timer.status = BZRTP_TIMER_OFF; /* store the commit packet in the channel context as it is needed later to check MAC */ zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID] = zrtpPacket; /* save the peer H2 */ memcpy(zrtpChannelContext->peerH[2], commitMessage->H2, 32); /* H2 */ /* we are receiver, set it in the context and update our selected algos */ zrtpChannelContext->role = BZRTP_ROLE_RESPONDER; zrtpChannelContext->hashAlgo = commitMessage->hashAlgo; zrtpChannelContext->cipherAlgo = commitMessage->cipherAlgo; zrtpChannelContext->authTagAlgo = commitMessage->authTagAlgo; zrtpChannelContext->keyAgreementAlgo = commitMessage->keyAgreementAlgo; zrtpChannelContext->sasAlgo = commitMessage->sasAlgo; bzrtp_updateCryptoFunctionPointers(zrtpChannelContext); /* if we have a self DHPart packet (means we are in DHM mode) we must rebuild the self DHPart packet to be responder and not initiator */ /* as responder we must swap the aux shared secret between responder and initiator as they are computed using the H3 and not a constant string */ if (zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID] != NULL) { uint8_t tmpBuffer[8]; bzrtpDHPartMessage_t *selfDHPart1Packet; int retval; memcpy(tmpBuffer, zrtpChannelContext->initiatorAuxsecretID, 8); memcpy(zrtpChannelContext->initiatorAuxsecretID, zrtpChannelContext->responderAuxsecretID, 8); memcpy(zrtpChannelContext->responderAuxsecretID, tmpBuffer, 8); /* responder self DHPart packet is DHPart1, change the type (we created a DHPart2) */ zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageType = MSGTYPE_DHPART1; /* change the shared secret ID to the responder one (we set them by default to the initiator's one) */ selfDHPart1Packet = (bzrtpDHPartMessage_t *)zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageData; memcpy(selfDHPart1Packet->rs1ID, zrtpContext->responderCachedSecretHash.rs1ID, 8); memcpy(selfDHPart1Packet->rs2ID, zrtpContext->responderCachedSecretHash.rs2ID, 8); memcpy(selfDHPart1Packet->auxsecretID, zrtpChannelContext->responderAuxsecretID, 8); memcpy(selfDHPart1Packet->pbxsecretID, zrtpContext->responderCachedSecretHash.pbxsecretID, 8); /* free the packet string and rebuild the packet */ free(zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString); zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString = NULL; retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID], zrtpChannelContext->selfSequenceNumber); if (retval == 0) { zrtpChannelContext->selfSequenceNumber++; } else { return retval; } } /* create the init event for next state */ initEvent.eventType = BZRTP_EVENT_INIT; initEvent.bzrtpPacketString = NULL; initEvent.bzrtpPacketStringLength = 0; initEvent.bzrtpPacket = NULL; initEvent.zrtpContext = zrtpContext; initEvent.zrtpChannelContext = zrtpChannelContext; /* if we are in PreShared or Multi mode, go to state_confirmation_responderSendingConfirm1 */ if ((zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { /* set next state to state_confirmation_responderSendingConfirm1 */ zrtpChannelContext->stateMachine = state_confirmation_responderSendingConfirm1; /* call it with the init event */ return zrtpChannelContext->stateMachine(initEvent); } else {/* if we are in DHM mode, goes to state_keyAgreement_responderSendingDHPart1 */ /* set next state to state_keyAgreement_responderSendingDHPart1 */ zrtpChannelContext->stateMachine = state_keyAgreement_responderSendingDHPart1; /* call it with the init event */ return zrtpChannelContext->stateMachine(initEvent); } } /** * @brief When a Hello message arrive from peer for the first time, we shall parse it to check if it match our configuration and act on the context * This message may arrives when in state state_discovery_init or state_discovery_waitingForHello. * - Find agreement on algo to use * - Check if we have retained secrets in cache matching the peer ZID * - if agreed on a DHM mode : compute the public value and prepare a DHPart2 packet(assume we are initiator, change later if needed) * - if agreed on a non-DHM mode : compute s0 and derive keys from it TODO * * @param[in] zrtpContext The current zrtp Context * @param[in/out] zrtpChannelContext The channel we are operating * @param[in] zrtpPacket The zrtpPacket received, it contains the hello message * * @return 0 on succes, error code otherwise */ int bzrtp_responseToHelloMessage(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext, bzrtpPacket_t *zrtpPacket) { int retval; int i; uint8_t peerSupportMultiChannel = 0; bzrtpPacket_t *helloACKPacket; bzrtpHelloMessage_t *helloMessage = (bzrtpHelloMessage_t *)zrtpPacket->messageData; /* check supported version of ZRTP protocol */ if (memcmp(helloMessage->version, ZRTP_VERSION, 3) != 0) { /* we support version 1.10 only but checking is done on 1.1? as explained in rfc section 4.1.1 */ bzrtp_freeZrtpPacket(zrtpPacket); return BZRTP_ERROR_UNSUPPORTEDZRTPVERSION; /* packet shall be ignored*/ } /* now check we have some algo in common. zrtpChannelContext will be updated with common algos if found */ retval = bzrtp_cryptoAlgoAgreement(zrtpContext, zrtpChannelContext, helloMessage); if(retval != 0) { bzrtp_freeZrtpPacket(zrtpPacket); return retval; } /* check if the peer accept MultiChannel */ for (i=0; ikc; i++) { if (helloMessage->supportedKeyAgreement[i] == ZRTP_KEYAGREEMENT_Mult) { peerSupportMultiChannel = 1; } } zrtpContext->peerSupportMultiChannel = peerSupportMultiChannel; /* copy into the channel context the relevant informations */ memcpy(zrtpContext->peerZID, helloMessage->ZID, 12); /* peer ZID */ memcpy(zrtpChannelContext->peerH[3], helloMessage->H3, 32); /* H3 */ zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; /* peer hello packet */ /* Extract from peerHello packet the version of bzrtp used by peer and store it in global context(needed later when exporting keys) * Bzrtp version started to be publicised in version 1.1, before that the client identifier was simply BZRTP and earlier version also have LINPHONE-ZRTPCPP * We must know this version in order to keep export key computation compatible between version as before v1.1 it was badly implemented * So basically check : * if the client identifier is BZRTP or LINPHONE-ZRTPCPP -> version 1.0 (use the old and incorrect way of creating an export key) * if BZRTPv1.1 -> version 1.1 (use RFC compliant way of creating the export key ) * if anything else peer use another library, set version to 0.0 and use RFC compliant way of creating the export key as peer library is expected to be RFC compliant */ /* This is BZRTP in its old version */ if ((strncmp(ZRTP_CLIENT_IDENTIFIERv1_0a, (char *)helloMessage->clientIdentifier, 16)==0) || (strncmp(ZRTP_CLIENT_IDENTIFIERv1_0b, (char *)helloMessage->clientIdentifier, 16)==0)){ zrtpContext->peerBzrtpVersion=0x010000; if (zrtpContext->zrtpCallbacks.bzrtp_statusMessage!=NULL && zrtpContext->zrtpCallbacks.bzrtp_messageLevel>=BZRTP_MESSAGE_WARNING) { /* use warning level as the client may really wants to know this */ zrtpContext->zrtpCallbacks.bzrtp_statusMessage(zrtpChannelContext->clientData, BZRTP_MESSAGE_WARNING, BZRTP_MESSAGE_PEERVERSIONOBSOLETE, (const char *)helloMessage->clientIdentifier); } } else if (strncmp(ZRTP_CLIENT_IDENTIFIERv1_1, (char *)helloMessage->clientIdentifier, 16)==0) { /* peer has the current version, everything is Ok */ zrtpContext->peerBzrtpVersion=0x010100; } else { /* peer uses another lib, we're probably not LIME compliant, log it */ zrtpContext->peerBzrtpVersion=0; if (zrtpContext->zrtpCallbacks.bzrtp_statusMessage!=NULL && zrtpContext->zrtpCallbacks.bzrtp_messageLevel>=BZRTP_MESSAGE_LOG) { zrtpContext->zrtpCallbacks.bzrtp_statusMessage(zrtpChannelContext->clientData, BZRTP_MESSAGE_LOG, BZRTP_MESSAGE_PEERNOTBZRTP, (const char *)helloMessage->clientIdentifier); } } /* now select mode according to context */ if ((zrtpContext->peerSupportMultiChannel) == 1 && (zrtpContext->ZRTPSess != NULL)) { /* if we support multichannel and already have a ZRTPSess key, switch to multichannel mode */ zrtpChannelContext->keyAgreementAlgo = ZRTP_KEYAGREEMENT_Mult; zrtpChannelContext->keyAgreementLength = 0; } else { /* we are not in multiStream mode, so we shall compute the hash of shared secrets */ /* get from cache, if relevant, the retained secrets associated to the peer ZID */ if (zrtpContext->cachedSecret.rs1 == NULL) { /* if we do not have already secret hashes in this session context. Note, they may be updated in cache file but they also will be in the context at the same time, so no need to parse the cache again */ bzrtp_getPeerAssociatedSecrets(zrtpContext, helloMessage->ZID); } /* now compute the retained secret hashes (secrets may be updated but not their hash) as in rfc section 4.3.1 */ if (zrtpContext->cachedSecret.rs1!=NULL) { zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.rs1, zrtpContext->cachedSecret.rs1Length, (uint8_t *)"Initiator", 9, 8, zrtpContext->initiatorCachedSecretHash.rs1ID); zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.rs1, zrtpContext->cachedSecret.rs1Length, (uint8_t *)"Responder", 9, 8, zrtpContext->responderCachedSecretHash.rs1ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->initiatorCachedSecretHash.rs1ID, 8); bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->responderCachedSecretHash.rs1ID, 8); } if (zrtpContext->cachedSecret.rs2!=NULL) { zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.rs2, zrtpContext->cachedSecret.rs2Length, (uint8_t *)"Initiator", 9, 8, zrtpContext->initiatorCachedSecretHash.rs2ID); zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.rs2, zrtpContext->cachedSecret.rs2Length, (uint8_t *)"Responder", 9, 8, zrtpContext->responderCachedSecretHash.rs2ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->initiatorCachedSecretHash.rs2ID, 8); bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->responderCachedSecretHash.rs2ID, 8); } if (zrtpContext->cachedSecret.pbxsecret!=NULL) { zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.pbxsecret, zrtpContext->cachedSecret.pbxsecretLength, (uint8_t *)"Initiator", 9, 8, zrtpContext->initiatorCachedSecretHash.pbxsecretID); zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.pbxsecret, zrtpContext->cachedSecret.pbxsecretLength, (uint8_t *)"Responder", 9, 8, zrtpContext->responderCachedSecretHash.pbxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->initiatorCachedSecretHash.pbxsecretID, 8); bctbx_rng_get(zrtpContext->RNGContext, zrtpContext->responderCachedSecretHash.pbxsecretID, 8); } /* if we have any transient auxiliary secret, append it to the one found in cache */ if (zrtpContext->transientAuxSecret!=NULL) { zrtpContext->cachedSecret.auxsecret = (uint8_t *)realloc(zrtpContext->cachedSecret.auxsecret, zrtpContext->cachedSecret.auxsecretLength + zrtpContext->transientAuxSecretLength); memcpy(zrtpContext->cachedSecret.auxsecret + zrtpContext->cachedSecret.auxsecretLength, zrtpContext->transientAuxSecret, zrtpContext->transientAuxSecretLength); zrtpContext->cachedSecret.auxsecretLength += zrtpContext->transientAuxSecretLength; } if (zrtpContext->cachedSecret.auxsecret!=NULL) { zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.auxsecret, zrtpContext->cachedSecret.auxsecretLength, zrtpChannelContext->selfH[3], 32, 8, zrtpChannelContext->initiatorAuxsecretID); zrtpChannelContext->hmacFunction(zrtpContext->cachedSecret.auxsecret, zrtpContext->cachedSecret.auxsecretLength, zrtpChannelContext->peerH[3], 32, 8, zrtpChannelContext->responderAuxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(zrtpContext->RNGContext, zrtpChannelContext->initiatorAuxsecretID, 8); bctbx_rng_get(zrtpContext->RNGContext, zrtpChannelContext->responderAuxsecretID, 8); } } /* When in PreShared mode Derive ZRTPSess, s0 from the retained secret and then all the other keys */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { /*TODO*/ } else if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult) { /* when in Multistream mode, do nothing, will derive s0 from ZRTPSess when we know who is initiator */ } else { /* when in DHM mode : Create the DHPart2 packet (that we then may change to DHPart1 if we ended to be the responder)*/ bzrtpPacket_t *selfDHPartPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_DHPART2, &retval); if (retval != 0) { return retval; /* no need to free the Hello message as it is attached to the context, it will be freed when destroying it */ } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, selfDHPartPacket, 0); /* we don't care now about sequence number as we just need to build the message to be able to insert a hash of it into the commit packet */ if (retval == 0) { /* ok, insert it in context as we need it to build the commit packet */ zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID] = selfDHPartPacket; } else { return retval; /* no need to free the Hello message as it is attached to the context, it will be freed when destroying it */ } } /* now respond to this Hello packet sending a Hello ACK */ helloACKPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpChannelContext, MSGTYPE_HELLOACK, &retval); if (retval != 0) { return retval; /* no need to free the Hello message as it is attached to the context, it will be freed when destroying it */ } retval = bzrtp_packetBuild(zrtpContext, zrtpChannelContext, helloACKPacket, zrtpChannelContext->selfSequenceNumber); if (retval != 0) { bzrtp_freeZrtpPacket(helloACKPacket); return retval; } else { /* send the message */ zrtpContext->zrtpCallbacks.bzrtp_sendData(zrtpChannelContext->clientData, helloACKPacket->packetString, helloACKPacket->messageLength+ZRTP_PACKET_OVERHEAD); zrtpChannelContext->selfSequenceNumber++; bzrtp_freeZrtpPacket(helloACKPacket); } return 0; } /** * @brief After the DHPart1 or DHPart2 arrives from peer, validity check and shared secret computation * call this function to compute s0, KDF Context, ZRTPSess, * * param[in] zrtpContext The context we are operation on(where to find the DHM context with the shared secret ready) * param[in] zrtpChannelContext The channel context we are operation on * * return 0 on success, error code otherwise */ int bzrtp_computeS0DHMMode(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { uint8_t *dataToHash; /* a buffer used to store concatened data to be hashed */ uint16_t hashDataLength; /* Length of the buffer */ uint16_t hashDataIndex; /* an index used while filling the buffer */ uint8_t *ZIDi; /* a pointer to the 12 bytes string initiator's ZID */ uint8_t *ZIDr; /* a pointer to the 12 bytes string responder's ZID */ uint8_t *totalHash; uint8_t *s1=NULL; /* s1 is rs1 if we have it, rs2 otherwise, or null if we do not have rs2 too */ uint32_t s1Length=0; uint8_t *s2=NULL; /* s2 is aux secret if we have it, null otherwise */ uint32_t s2Length=0; uint8_t *s3=NULL; /* s3 is pbx secret if we have it, null otherwise */ uint32_t s3Length=0; /* first compute the total_hash as in rfc section 4.4.1.4 * total_hash = hash(Hello of responder || Commit || DHPart1 || DHPart2) * total_hash length depends on the agreed hash algo */ if (zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { hashDataLength = zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength; dataToHash = (uint8_t *)malloc(hashDataLength*sizeof(uint8_t)); hashDataIndex = 0; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength); ZIDi = zrtpContext->peerZID; ZIDr = zrtpContext->selfZID; } else { /* we are initiator */ hashDataLength = zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength; dataToHash = (uint8_t *)malloc(hashDataLength*sizeof(uint8_t)); hashDataIndex = 0; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength); hashDataIndex +=zrtpChannelContext->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength); ZIDi = zrtpContext->selfZID; ZIDr = zrtpContext->peerZID; } totalHash = (uint8_t *)malloc(zrtpChannelContext->hashLength); zrtpChannelContext->hashFunction(dataToHash, hashDataLength, zrtpChannelContext->hashLength, totalHash); free(dataToHash); /* compute KDFContext = (ZIDi || ZIDr || total_hash) and set it in the channel context */ zrtpChannelContext->KDFContextLength = 24+zrtpChannelContext->hashLength; /* 24 for two 12 bytes ZID */ zrtpChannelContext->KDFContext = (uint8_t *)malloc(zrtpChannelContext->KDFContextLength*sizeof(uint8_t)); memcpy(zrtpChannelContext->KDFContext, ZIDi, 12); /* ZIDi*/ memcpy(zrtpChannelContext->KDFContext+12, ZIDr, 12); /* ZIDr */ memcpy(zrtpChannelContext->KDFContext+24, totalHash, zrtpChannelContext->hashLength); /* total Hash*/ free(totalHash); /* total hash is not needed anymore, get it from KDF Context in s0 computation */ /* compute s0 = hash(counter || DHResult || "ZRTP-HMAC-KDF" || ZIDi || ZIDr || total_hash || len(s1) || s1 || len(s2) || s2 || len(s3) || s3) * counter is a fixed 32 bits integer in big endian set to 1 : 0x00000001 */ /* get s1 from rs1 or rs2 */ if (zrtpContext->cachedSecret.rs1 != NULL) { /* if there is a s1 (already validated when received the DHpacket) */ s1 = zrtpContext->cachedSecret.rs1; s1Length = zrtpContext->cachedSecret.rs1Length; } else if (zrtpContext->cachedSecret.rs2 != NULL) { /* otherwise if there is a s2 (already validated when received the DHpacket) */ s1 = zrtpContext->cachedSecret.rs2; s1Length = zrtpContext->cachedSecret.rs2Length; } /* s2 is the auxsecret */ s2 = zrtpContext->cachedSecret.auxsecret; /* this may be null if no match or no aux secret where found */ s2Length = zrtpContext->cachedSecret.auxsecretLength; /* this may be 0 if no match or no aux secret where found */ /* s3 is the pbxsecret */ s3 = zrtpContext->cachedSecret.pbxsecret; /* this may be null if no match or no pbx secret where found */ s3Length = zrtpContext->cachedSecret.pbxsecretLength; /* this may be 0 if no match or no pbx secret where found */ hashDataLength = 4/*counter*/ + zrtpChannelContext->keyAgreementLength/*DHResult*/+13/*ZRTP-HMAC-KDF string*/ + 12/*ZIDi*/ + 12/*ZIDr*/ + zrtpChannelContext->hashLength/*total_hash*/ + 4/*len(s1)*/ +s1Length/*s1*/ + 4/*len(s2)*/ +s2Length/*s2*/ + 4/*len(s3)*/ + s3Length/*s3*/; dataToHash = (uint8_t *)malloc(hashDataLength*sizeof(uint8_t)); /* counter */ dataToHash[0] = 0x00; dataToHash[1] = 0x00; dataToHash[2] = 0x00; dataToHash[3] = 0x01; hashDataIndex = 4; if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)zrtpContext->keyAgreementContext; memcpy(dataToHash+hashDataIndex, DHMContext->key, zrtpChannelContext->keyAgreementLength); } if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)zrtpContext->keyAgreementContext; memcpy(dataToHash+hashDataIndex, ECDHContext->sharedSecret, zrtpChannelContext->keyAgreementLength); } hashDataIndex += zrtpChannelContext->keyAgreementLength; memcpy(dataToHash+hashDataIndex, "ZRTP-HMAC-KDF", 13); hashDataIndex += 13; /* KDF Context is already ZIDi || ZIDr || total_hash use it directly */ memcpy(dataToHash+hashDataIndex, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength); hashDataIndex += zrtpChannelContext->KDFContextLength; dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s1Length&0xFF); if (s1!=NULL) { memcpy(dataToHash+hashDataIndex, s1, s1Length); hashDataIndex += s1Length; } dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s2Length&0xFF); if (s2!=NULL) { memcpy(dataToHash+hashDataIndex, s2, s2Length); hashDataIndex += s2Length; } dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s3Length&0xFF); if (s3!=NULL) { memcpy(dataToHash+hashDataIndex, s3, s3Length); hashDataIndex += s3Length; } /* clean local s1,s2,s3 pointers as they are not needed anymore */ s1=NULL; s2=NULL; s3=NULL; zrtpChannelContext->s0 = (uint8_t *)malloc(zrtpChannelContext->hashLength*sizeof(uint8_t)); zrtpChannelContext->hashFunction(dataToHash, hashDataLength, zrtpChannelContext->hashLength, zrtpChannelContext->s0); free(dataToHash); /* now compute the ZRTPSession key : section 4.5.2 * ZRTPSess = KDF(s0, "ZRTP Session Key", KDF_Context, negotiated hash length)*/ zrtpContext->ZRTPSessLength=zrtpChannelContext->hashLength; /* must be set to the length of negotiated hash */ zrtpContext->ZRTPSess = (uint8_t *)malloc(zrtpContext->ZRTPSessLength*sizeof(uint8_t)); bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"ZRTP Session Key", 16, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->hashLength, zrtpChannelContext->hmacFunction, zrtpContext->ZRTPSess); /* clean the DHM context (secret and key shall be erased by this operation) */ if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DestroyDHMContext((bctbx_DHMContext_t *)(zrtpContext->keyAgreementContext)); } if (zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || zrtpContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_DestroyECDHContext((bctbx_ECDHContext_t *)(zrtpContext->keyAgreementContext)); } zrtpContext->keyAgreementContext = NULL; zrtpContext->keyAgreementAlgo = ZRTP_UNSET_ALGO; /* now derive the other keys */ return bzrtp_deriveKeysFromS0(zrtpContext, zrtpChannelContext); } /** * @brief In multistream mode, when we must send a confirm1 or receive a confirm1 for the first time, call the function to compute * s0, KDF context and derive mac and srtp keys * * param[in] zrtpContext The context we are operation on(where to find the ZRTPSess) * param[in] zrtpChannelContext The channel context we are operation on * * return 0 on success, error code otherwise */ int bzrtp_computeS0MultiStreamMode(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { uint8_t *dataToHash; /* a buffer used to store concatened data to be hashed */ uint16_t hashDataLength; /* Length of the buffer */ uint16_t hashDataIndex; /* an index used while filling the buffer */ uint8_t *ZIDi; /* a pointer to the 12 bytes string initiator's ZID */ uint8_t *ZIDr; /* a pointer to the 12 bytes string responder's ZID */ uint8_t *totalHash; int retval; /* compute the total hash as in rfc section 4.4.3.2 total_hash = hash(Hello of responder || Commit) */ if (zrtpChannelContext->role == BZRTP_ROLE_RESPONDER) { /* if we are responder */ hashDataLength = zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; dataToHash = (uint8_t *)malloc(hashDataLength*sizeof(uint8_t)); hashDataIndex = 0; memcpy(dataToHash, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += zrtpChannelContext->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); ZIDi = zrtpContext->peerZID; ZIDr = zrtpContext->selfZID; } else { /* if we are initiator */ hashDataLength = zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength + zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; dataToHash = (uint8_t *)malloc(hashDataLength*sizeof(uint8_t)); hashDataIndex = 0; memcpy(dataToHash, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += zrtpChannelContext->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, zrtpChannelContext->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); ZIDi = zrtpContext->selfZID; ZIDr = zrtpContext->peerZID; } totalHash = (uint8_t *)malloc(zrtpChannelContext->hashLength); zrtpChannelContext->hashFunction(dataToHash, hashDataLength, zrtpChannelContext->hashLength, totalHash); free(dataToHash); /* compute KDFContext = (ZIDi || ZIDr || total_hash) and set it in the channel context */ zrtpChannelContext->KDFContextLength = 24+zrtpChannelContext->hashLength; /* 24 for two 12 bytes ZID */ zrtpChannelContext->KDFContext = (uint8_t *)malloc(zrtpChannelContext->KDFContextLength*sizeof(uint8_t)); memcpy(zrtpChannelContext->KDFContext, ZIDi, 12); /* ZIDi*/ memcpy(zrtpChannelContext->KDFContext+12, ZIDr, 12); /* ZIDr */ memcpy(zrtpChannelContext->KDFContext+24, totalHash, zrtpChannelContext->hashLength); /* total Hash*/ free(totalHash); /* total hash is not needed anymore, get it from KDF Context in s0 computation */ /* compute s0 as in rfc section 4.4.3.2 s0 = KDF(ZRTPSess, "ZRTP MSK", KDF_Context, negotiated hash length) */ zrtpChannelContext->s0 = (uint8_t *)malloc(zrtpChannelContext->hashLength*sizeof(uint8_t)); retval = bzrtp_keyDerivationFunction(zrtpContext->ZRTPSess, zrtpContext->ZRTPSessLength, (uint8_t *)"ZRTP MSK", 8, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->hashLength, zrtpChannelContext->hmacFunction, zrtpChannelContext->s0); if (retval != 0) { return retval; } /* now derive the other keys */ return bzrtp_deriveKeysFromS0(zrtpContext, zrtpChannelContext); } /** * @brief This function is called after s0 (and ZRTPSess when non in Multistream mode) have been computed to derive the other keys * Keys computed are: mackeyi, mackeyr, zrtpkeyi and zrtpkeyr, srtpkeys and salt * * param[in] zrtpContext The context we are operation on(contains ZRTPSess) * param[in/out] zrtpChannelContext The channel context we are operation on(contains s0 and will get the computed keys) * * return 0 on success, error code otherwise * */ int bzrtp_deriveKeysFromS0(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { int retval = 0; /* allocate memory for mackeyi, mackeyr, zrtpkeyi, zrtpkeyr */ zrtpChannelContext->mackeyi = (uint8_t *)malloc(zrtpChannelContext->hashLength*(sizeof(uint8_t))); zrtpChannelContext->mackeyr = (uint8_t *)malloc(zrtpChannelContext->hashLength*(sizeof(uint8_t))); zrtpChannelContext->zrtpkeyi = (uint8_t *)malloc(zrtpChannelContext->cipherKeyLength*(sizeof(uint8_t))); zrtpChannelContext->zrtpkeyr = (uint8_t *)malloc(zrtpChannelContext->cipherKeyLength*(sizeof(uint8_t))); /* derive the keys according to rfc section 4.5.3 */ /* mackeyi = KDF(s0, "Initiator HMAC key", KDF_Context, negotiated hash length)*/ retval = bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Initiator HMAC key", 18, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->hashLength, zrtpChannelContext->hmacFunction, zrtpChannelContext->mackeyi); /* mackeyr = KDF(s0, "Responder HMAC key", KDF_Context, negotiated hash length) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Responder HMAC key", 18, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->hashLength, zrtpChannelContext->hmacFunction, zrtpChannelContext->mackeyr); /* zrtpkeyi = KDF(s0, "Initiator ZRTP key", KDF_Context, negotiated AES key length) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Initiator ZRTP key", 18, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->cipherKeyLength, zrtpChannelContext->hmacFunction, zrtpChannelContext->zrtpkeyi); /* zrtpkeyr = KDF(s0, "Responder ZRTP key", KDF_Context, negotiated AES key length) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Responder ZRTP key", 18, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->cipherKeyLength, zrtpChannelContext->hmacFunction, zrtpChannelContext->zrtpkeyr); return retval; } /** * @brief This function is called after confirm1 is received by initiator or confirm2 by responder * Keys computed are: srtp self and peer keys and salt, SAS(if mode is not multistream). * The whole bzrtpSrtpSecrets_t structure is ready after this call * * param[in] zrtpContext The context we are operation on * param[in/out] zrtpChannelContext The channel context we are operation on(contains s0 and will get the computed keys) * * return 0 on success, error code otherwise * */ int bzrtp_deriveSrtpKeysFromS0(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { int retval = 0; /* allocate memory */ uint8_t *srtpkeyi = (uint8_t *)malloc(zrtpChannelContext->cipherKeyLength*sizeof(uint8_t)); uint8_t *srtpkeyr = (uint8_t *)malloc(zrtpChannelContext->cipherKeyLength*sizeof(uint8_t)); uint8_t *srtpsalti = (uint8_t *)malloc(14*sizeof(uint8_t));/* salt length is defined to be 112 bits(14 bytes) in rfc section 4.5.3 */ uint8_t *srtpsaltr = (uint8_t *)malloc(14*sizeof(uint8_t));/* salt length is defined to be 112 bits(14 bytes) in rfc section 4.5.3 */ /* compute keys and salts according to rfc section 4.5.3 */ /* srtpkeyi = KDF(s0, "Initiator SRTP master key", KDF_Context, negotiated AES key length) */ retval = bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Initiator SRTP master key", 25, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->cipherKeyLength, zrtpChannelContext->hmacFunction, srtpkeyi); /* srtpsalti = KDF(s0, "Initiator SRTP master salt", KDF_Context, 112) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Initiator SRTP master salt", 26, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, 14, zrtpChannelContext->hmacFunction, srtpsalti); /* srtpkeyr = KDF(s0, "Responder SRTP master key", KDF_Context, negotiated AES key length) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Responder SRTP master key", 25, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, zrtpChannelContext->cipherKeyLength, zrtpChannelContext->hmacFunction, srtpkeyr); /* srtpsaltr = KDF(s0, "Responder SRTP master salt", KDF_Context, 112) */ retval += bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"Responder SRTP master salt", 26, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, 14, zrtpChannelContext->hmacFunction, srtpsaltr); if (retval!=0) { free(srtpkeyi); free(srtpkeyr); free(srtpsalti); free(srtpsaltr); return retval; } /* Associate responder or initiator to self or peer. Self is used to locally encrypt and peer to decrypt */ if (zrtpChannelContext->role == BZRTP_ROLE_INITIATOR) { /* we use keyi to encrypt and keyr to decrypt */ zrtpChannelContext->srtpSecrets.selfSrtpKey = srtpkeyi; zrtpChannelContext->srtpSecrets.selfSrtpSalt = srtpsalti; zrtpChannelContext->srtpSecrets.peerSrtpKey = srtpkeyr; zrtpChannelContext->srtpSecrets.peerSrtpSalt = srtpsaltr; } else { /* we use keyr to encrypt and keyi to decrypt */ zrtpChannelContext->srtpSecrets.selfSrtpKey = srtpkeyr; zrtpChannelContext->srtpSecrets.selfSrtpSalt = srtpsaltr; zrtpChannelContext->srtpSecrets.peerSrtpKey = srtpkeyi; zrtpChannelContext->srtpSecrets.peerSrtpSalt = srtpsalti; } /* Set the length in secrets structure */ zrtpChannelContext->srtpSecrets.selfSrtpKeyLength = zrtpChannelContext->cipherKeyLength; zrtpChannelContext->srtpSecrets.selfSrtpSaltLength = 14; /* salt length is defined to be 112 bits(14 bytes) in rfc section 4.5.3 */ zrtpChannelContext->srtpSecrets.peerSrtpKeyLength = zrtpChannelContext->cipherKeyLength; zrtpChannelContext->srtpSecrets.peerSrtpSaltLength = 14; /* salt length is defined to be 112 bits(14 bytes) in rfc section 4.5.3 */ /* Set the used algo in secrets structure */ zrtpChannelContext->srtpSecrets.cipherAlgo = zrtpChannelContext->cipherAlgo; zrtpChannelContext->srtpSecrets.cipherKeyLength = zrtpChannelContext->cipherKeyLength; zrtpChannelContext->srtpSecrets.authTagAlgo = zrtpChannelContext->authTagAlgo; /* for information purpose, add the negotiated algorithm */ zrtpChannelContext->srtpSecrets.hashAlgo = zrtpChannelContext->hashAlgo; zrtpChannelContext->srtpSecrets.keyAgreementAlgo = zrtpChannelContext->keyAgreementAlgo; zrtpChannelContext->srtpSecrets.sasAlgo = zrtpChannelContext->sasAlgo; /* compute the SAS according to rfc section 4.5.2 sashash = KDF(s0, "SAS", KDF_Context, 256) */ if (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Mult) { /* only when not in Multistream mode */ uint8_t sasHash[32]; /* length of hash is 256 bits -> 32 bytes */ uint32_t sasValue; retval = bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"SAS", 3, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, 32, zrtpChannelContext->hmacFunction, sasHash); if (retval!=0) { return retval; } /* now get it into a char according to the selected algo */ sasValue = ((uint32_t)sasHash[0]<<24) | ((uint32_t)sasHash[1]<<16) | ((uint32_t)sasHash[2]<<8) | ((uint32_t)(sasHash[3])); zrtpChannelContext->srtpSecrets.sasLength = zrtpChannelContext->sasLength; zrtpChannelContext->srtpSecrets.sas = (char *)malloc((zrtpChannelContext->sasLength)*sizeof(char)); /*this shall take in account the selected representation algo for SAS */ zrtpChannelContext->sasFunction(sasValue, zrtpChannelContext->srtpSecrets.sas, zrtpChannelContext->sasLength); /* set also the cache mismtach flag in srtpSecrets structure, may occurs only on the first channel */ if (zrtpContext->cacheMismatchFlag!=0) { zrtpChannelContext->srtpSecrets.cacheMismatch = 1; } } return 0; } /* * @brief Compute the new rs1 and update the cached secrets according to rfc section 4.6.1 * * param[in] zrtpContext The context we are operation on * param[in/out] zrtpChannelContext The channel context we are operation on(contains s0) * * return 0 on success, error code otherwise */ int bzrtp_updateCachedSecrets(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t *zrtpChannelContext) { const char *colNames[] = {"rs1", "rs2"}; uint8_t *colValues[2] = {NULL, NULL}; size_t colLength[2] = {RETAINED_SECRET_LENGTH,0}; /* if this channel context is in multistream mode, do nothing */ if (zrtpChannelContext->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult) { /* destroy s0 */ bzrtp_DestroyKey(zrtpChannelContext->s0, zrtpChannelContext->hashLength, zrtpContext->RNGContext); free(zrtpChannelContext->s0); zrtpChannelContext->s0 = NULL; return 0; } /* if we had a cache mismatch, the update must be delayed until Sas confirmation by user, just do nothing, the update function will be called again when done */ if (zrtpContext->cacheMismatchFlag == 1) { return 0; } /* if this channel context is in DHM mode, backup rs1 in rs2 if it exists */ if (zrtpChannelContext->keyAgreementAlgo != ZRTP_KEYAGREEMENT_Prsh) { if (zrtpContext->cachedSecret.rs1 != NULL) { /* store rs2 pointer and length to be passed to cache_write function (otherwise, keep NULL and the rs2 in cache will be erased) */ colValues[1] = zrtpContext->cachedSecret.rs1; colLength[1] = RETAINED_SECRET_LENGTH; } } /* compute rs1 = KDF(s0, "retained secret", KDF_Context, 256) */ zrtpContext->cachedSecret.rs1 = (uint8_t *)malloc(RETAINED_SECRET_LENGTH); /* Allocate a new buffer for rs1, the old one if exists if still pointed at by colValues[1] */ zrtpContext->cachedSecret.rs1Length = RETAINED_SECRET_LENGTH; bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"retained secret", 15, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, RETAINED_SECRET_LENGTH, zrtpChannelContext->hmacFunction, zrtpContext->cachedSecret.rs1); colValues[0]=zrtpContext->cachedSecret.rs1; /* before writing into cache, we must check we have the zuid correctly set, if not (it's our first successfull exchange with peer), insert it*/ if (zrtpContext->zuid==0) { bzrtp_cache_getZuid((void *)zrtpContext->zidCache, zrtpContext->selfURI, zrtpContext->peerURI, zrtpContext->peerZID, BZRTP_ZIDCACHE_INSERT_ZUID, &zrtpContext->zuid, zrtpContext->zidCacheMutex); } bzrtp_cache_write_active(zrtpContext, "zrtp", colNames, colValues, colLength, 2); /* if exist, call the callback function to perform custom cache operation that may use s0(writing exported key into cache) */ if (zrtpContext->zrtpCallbacks.bzrtp_contextReadyForExportedKeys != NULL) { zrtpContext->zrtpCallbacks.bzrtp_contextReadyForExportedKeys(zrtpChannelContext->clientData, zrtpContext->zuid, zrtpChannelContext->role); /* destroy exportedKey if we computed one */ if (zrtpContext->exportedKey!=NULL) { bzrtp_DestroyKey(zrtpContext->exportedKey, zrtpContext->exportedKeyLength, zrtpContext->RNGContext); free(zrtpContext->exportedKey); zrtpContext->exportedKey=NULL; } } /* destroy s0 */ bzrtp_DestroyKey(zrtpChannelContext->s0, zrtpChannelContext->hashLength, zrtpContext->RNGContext); free(zrtpChannelContext->s0); zrtpChannelContext->s0 = NULL; /* destroy all cached keys in context they are not needed anymore (multistream mode doesn't use them to compute s0) */ if (colValues[1] != NULL) { /* destroy the old rs1 whose pointer was saved in colValues[1] before secrets.rs1 being crushed by the new rs1 */ bzrtp_DestroyKey(colValues[1], zrtpContext->cachedSecret.rs1Length, zrtpContext->RNGContext); free(colValues[1]); colValues[1]=NULL; } if (zrtpContext->cachedSecret.rs1!=NULL) { bzrtp_DestroyKey(zrtpContext->cachedSecret.rs1, zrtpContext->cachedSecret.rs1Length, zrtpContext->RNGContext); free(zrtpContext->cachedSecret.rs1); zrtpContext->cachedSecret.rs1 = NULL; } if (zrtpContext->cachedSecret.rs2!=NULL) { bzrtp_DestroyKey(zrtpContext->cachedSecret.rs2, zrtpContext->cachedSecret.rs2Length, zrtpContext->RNGContext); free(zrtpContext->cachedSecret.rs2); zrtpContext->cachedSecret.rs2 = NULL; } if (zrtpContext->cachedSecret.auxsecret!=NULL) { bzrtp_DestroyKey(zrtpContext->cachedSecret.auxsecret, zrtpContext->cachedSecret.auxsecretLength, zrtpContext->RNGContext); free(zrtpContext->cachedSecret.auxsecret); zrtpContext->cachedSecret.auxsecret = NULL; } if (zrtpContext->cachedSecret.pbxsecret!=NULL) { bzrtp_DestroyKey(zrtpContext->cachedSecret.pbxsecret, zrtpContext->cachedSecret.pbxsecretLength, zrtpContext->RNGContext); free(zrtpContext->cachedSecret.pbxsecret); zrtpContext->cachedSecret.pbxsecret = NULL; } return 0; } bzrtp-4.4.13/src/zidCache.c000066400000000000000000001464721364144501400154310ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "typedef.h" #include #include "cryptoUtils.h" #include "zidCache.h" #ifdef ZIDCACHE_ENABLED #include "sqlite3.h" #ifdef HAVE_LIBXML2 #include #include #endif /* HAVE_LIBXML2 */ #ifdef _WIN32 #include #else #include #endif /* define a version number for the DB schema as an interger MMmmpp */ /* current version is 0.0.2 */ /* Changelog: * version 0.0.2 : Add a the active flag in the ziduri table * version 0.0.1 : Initial version */ #define ZIDCACHE_DBSCHEMA_VERSION_NUMBER 0x000002 static int callback_getSelfZID(void *data, int argc, char **argv, char **colName){ uint8_t **selfZID = (uint8_t **)data; /* we selected zid only, it must then be in argv[0] */ if (argv[0]) { *selfZID = (uint8_t *)malloc(12*sizeof(uint8_t)); memcpy(*selfZID, argv[0], 12); } return 0; } static int callback_getUserVersion(void *data, int argc, char **argv, char **colName){ int *userVersion = (int *)data; if (argv[0]) { *userVersion = atoi(argv[0]); } return 0; } /** * @brief Update the database schema from version 0.0.1 to version 0.0.2 * * Add an integer field 'active' defaulting to 0 in the ziduri table * * @param[in/out] db The sqlite pointer to the table to be updated * * @return 0 on success, BZRTP_ZIDCACHE_UNABLETOUPDATE otherwise */ static int bzrtp_cache_update_000001_to_000002(sqlite3 *db) { /* create the ziduri table */ int ret; char* errmsg=NULL; ret=sqlite3_exec(db,"ALTER TABLE ziduri ADD COLUMN active INTEGER DEFAULT 0;", 0, 0, &errmsg); if(ret != SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOUPDATE; } return 0; } /* ZID cache is split in several tables * ziduri : zuid(unique key) | ZID | selfuri | peeruri | active * zuid(ZID/URI binding id) will be used for fastest access to the cache, it binds a local user(self uri/self ZID) to a peer identified both by URI and ZID * active is a flag set to spot the last active peer device associated to an URI. Each time a ZRTP exchange takes place, the active flag is set to one and all other rows with the same peeruri are set to zero * self ZID is stored in this table too in a record having 'self' as peer uri, each local user(uri) has a different ZID * * All values except zuid in the following tables are blob, actual integers are split and stored in big endian by callers * zrtp : zuid(as foreign key) | rs1 | rs2 | aux secret | pbx secret | pvs flag * lime : zuid(as foreign key) | sndKey | rcvKey | sndSId | rcvSId | snd Index | rcv Index | valid */ static int bzrtp_initCache_impl(void *dbPointer) { char* errmsg=NULL; int ret; char *sql; sqlite3_stmt *stmt = NULL; int userVersion=-1; sqlite3 *db = (sqlite3 *)dbPointer; int retval = 0; if (dbPointer == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } /* get current db schema version (user_version pragma in sqlite )*/ sql = sqlite3_mprintf("PRAGMA user_version;"); ret = sqlite3_exec(db, sql, callback_getUserVersion, &userVersion, &errmsg); sqlite3_free(sql); if (ret!=SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOREAD; } /* Here check version number and provide upgrade is needed */ if (userVersion != ZIDCACHE_DBSCHEMA_VERSION_NUMBER) { if (userVersion > ZIDCACHE_DBSCHEMA_VERSION_NUMBER) { /* nothing to do if we encounter a superior version number than expected, just hope it is compatible */ //TODO: Log this event } else { /* Perform update if needed */ switch ( userVersion ) { case 0x000000 : /* nothing to do this is base creation */ break; case 0x000001 : ret = bzrtp_cache_update_000001_to_000002(db); if (ret != 0) { return ret; } break; default : /* nothing particular to do but it shall not append and we shall warn the dev: db schema version was upgraded but no migration function is executed */ break; } /* update the schema version in DB metadata */ sql = sqlite3_mprintf("PRAGMA user_version = %d;",ZIDCACHE_DBSCHEMA_VERSION_NUMBER); ret = sqlite3_prepare(db, sql, -1, &stmt, NULL); sqlite3_free(sql); if (ret != SQLITE_OK) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } ret = sqlite3_step(stmt); if (ret != SQLITE_DONE) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_finalize(stmt); /* setup return value : SETUP for a brand new populated cache, update if we updated the scheme */ if (userVersion == 0) { retval = BZRTP_CACHE_SETUP; } else { retval = BZRTP_CACHE_UPDATE; } } } /* make sure foreign key are turned ON on this connection */ ret = sqlite3_prepare(db, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); if (ret != SQLITE_OK) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } ret = sqlite3_step(stmt); if (ret != SQLITE_DONE) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_finalize(stmt); /* if we have an update, we can exit now */ if (retval == BZRTP_CACHE_UPDATE) { return BZRTP_CACHE_UPDATE; } /* create the ziduri table */ ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS ziduri (" "zuid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," "zid BLOB NOT NULL DEFAULT '000000000000'," "selfuri TEXT NOT NULL DEFAULT 'unset'," "peeruri TEXT NOT NULL DEFAULT 'unset'," "active INTEGER DEFAULT 0" ");", 0,0,&errmsg); if(ret != SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOUPDATE; } /* check/create the zrtp table */ ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS zrtp (" "zuid INTEGER NOT NULL DEFAULT 0 UNIQUE," "rs1 BLOB DEFAULT NULL," "rs2 BLOB DEFAULT NULL," "aux BLOB DEFAULT NULL," "pbx BLOB DEFAULT NULL," "pvs BLOB DEFAULT NULL," "FOREIGN KEY(zuid) REFERENCES ziduri(zuid) ON UPDATE CASCADE ON DELETE CASCADE" ");", 0,0,&errmsg); if(ret != SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOUPDATE; } /* check/create the lime table */ ret=sqlite3_exec(db,"CREATE TABLE IF NOT EXISTS lime (" "zuid INTEGER NOT NULL DEFAULT 0 UNIQUE," "sndKey BLOB DEFAULT NULL," "rcvKey BLOB DEFAULT NULL," "sndSId BLOB DEFAULT NULL," "rcvSId BLOB DEFAULT NULL," "sndIndex BLOB DEFAULT NULL," "rcvIndex BLOB DEFAULT NULL," "valid BLOB DEFAULT NULL," "FOREIGN KEY(zuid) REFERENCES ziduri(zuid) ON UPDATE CASCADE ON DELETE CASCADE" ");", 0,0,&errmsg); if(ret != SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOUPDATE; } return retval; } /* non locking database version of the previous function, is deprecated but kept for compatibility */ int bzrtp_initCache(void *dbPointer) { return bzrtp_initCache_impl(dbPointer); } /* locking database version of the previous function */ int bzrtp_initCache_lock(void *dbPointer, bctbx_mutex_t *zidCacheMutex) { int retval; if (dbPointer != NULL && zidCacheMutex != NULL) { bctbx_mutex_lock(zidCacheMutex); sqlite3_exec((sqlite3 *)dbPointer, "BEGIN TRANSACTION;", NULL, NULL, NULL); retval = bzrtp_initCache_impl(dbPointer); if (retval == 0 || retval == BZRTP_CACHE_UPDATE || retval == BZRTP_CACHE_SETUP) { sqlite3_exec((sqlite3 *)dbPointer, "COMMIT;", NULL, NULL, NULL); } else { sqlite3_exec((sqlite3 *)dbPointer, "ROLLBACK;", NULL, NULL, NULL); } bctbx_mutex_unlock(zidCacheMutex); return retval; } else { return bzrtp_initCache_impl(dbPointer); } } static int bzrtp_getSelfZID_impl(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext) { char* errmsg=NULL; int ret; char *stmt; uint8_t *localZID = NULL; sqlite3 *db = (sqlite3 *)dbPointer; if (dbPointer == NULL) { /* we are running cacheless, generate a random ZID if we have a RNG*/ if (RNGContext != NULL) { bctbx_rng_get(RNGContext, selfZID, 12); return 0; } else { return BZRTP_CACHE_DATA_NOTFOUND; } } /* check/create the self zid in ziduri table, ORDER BY is just to ensure consistent return in case of inconsistent table(with several self ZID for the same selfuri) */ stmt = sqlite3_mprintf("SELECT zid FROM ziduri WHERE selfuri=%Q AND peeruri='self' ORDER BY zuid LIMIT 1;",selfURI); ret = sqlite3_exec(db,stmt,callback_getSelfZID,&localZID,&errmsg); if (ret != SQLITE_OK) { sqlite3_free(errmsg); return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_free(stmt); /* Do we have a self ZID in cache? */ if (localZID == NULL) { uint8_t generatedZID[12]; sqlite3_stmt *insertStatement = NULL; /* generate a ZID if we can */ if (RNGContext != NULL) { bctbx_rng_get(RNGContext, generatedZID, 12); } else { return BZRTP_CACHE_DATA_NOTFOUND; } /* insert it in the table */ ret = sqlite3_prepare_v2(db, "INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);", -1, &insertStatement, NULL); if (ret != SQLITE_OK) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_bind_blob(insertStatement, 1, generatedZID, 12, SQLITE_TRANSIENT); sqlite3_bind_text(insertStatement, 2, selfURI,-1,SQLITE_TRANSIENT); sqlite3_bind_text(insertStatement, 3, "self",-1,SQLITE_TRANSIENT); ret = sqlite3_step(insertStatement); if (ret!=SQLITE_DONE) { return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_finalize(insertStatement); /* copy it in the output buffer */ memcpy(selfZID, generatedZID,12); } else { /* we found a ZID, copy it in the output buffer */ memcpy(selfZID, localZID, 12); free(localZID); } return 0; } /* non locking database version of the previous function, is deprecated but kept for compatibility */ int bzrtp_getSelfZID(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext) { return bzrtp_getSelfZID_impl(dbPointer, selfURI, selfZID, RNGContext); } /* locking database version of the previous function */ int bzrtp_getSelfZID_lock(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext, bctbx_mutex_t *zidCacheMutex) { int retval; if (dbPointer != NULL && zidCacheMutex != NULL) { bctbx_mutex_lock(zidCacheMutex); sqlite3_exec((sqlite3 *)dbPointer, "BEGIN TRANSACTION;", NULL, NULL, NULL); retval = bzrtp_getSelfZID_impl(dbPointer, selfURI, selfZID, RNGContext); if (retval == 0) { sqlite3_exec((sqlite3 *)dbPointer, "COMMIT;", NULL, NULL, NULL); } else { sqlite3_exec((sqlite3 *)dbPointer, "ROLLBACK;", NULL, NULL, NULL); } bctbx_mutex_unlock(zidCacheMutex); return retval; } else { return bzrtp_getSelfZID_impl(dbPointer, selfURI, selfZID, RNGContext); } } /** * @brief Parse the cache to find secrets associated to the given ZID, set them and their length in the context if they are found * * @param[in/out] context the current context, used to get the negotiated Hash algorithm, cache db, peerURI and store results * @param[in] peerZID a byte array of the peer ZID * * return 0 on succes, error code otherwise */ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]) { char *stmt = NULL; int ret; sqlite3_stmt *sqlStmt = NULL; int length =0; if (context == NULL) { return BZRTP_ZIDCACHE_INVALID_CONTEXT; } /* resert cached secret buffer */ free(context->cachedSecret.rs1); free(context->cachedSecret.rs2); free(context->cachedSecret.pbxsecret); free(context->cachedSecret.auxsecret); context->cachedSecret.rs1 = NULL; context->cachedSecret.rs1Length = 0; context->cachedSecret.rs2 = NULL; context->cachedSecret.rs2Length = 0; context->cachedSecret.pbxsecret = NULL; context->cachedSecret.pbxsecretLength = 0; context->cachedSecret.auxsecret = NULL; context->cachedSecret.auxsecretLength = 0; context->cachedSecret.previouslyVerifiedSas = 0; /* are we going cacheless at runtime */ if (context->zidCache == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } if (context->zidCacheMutex != NULL) { bctbx_mutex_lock(context->zidCacheMutex); } /* get all secrets from zrtp table, ORDER BY is just to ensure consistent return in case of inconsistent table) */ stmt = sqlite3_mprintf("SELECT z.zuid, z.rs1, z.rs2, z.aux, z.pbx, z.pvs FROM ziduri as zu INNER JOIN zrtp as z ON z.zuid=zu.zuid WHERE zu.selfuri=? AND zu.peeruri=? AND zu.zid=? ORDER BY zu.zuid LIMIT 1;"); ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmt, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_text(sqlStmt, 1, context->selfURI,-1,SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 2, context->peerURI,-1,SQLITE_TRANSIENT); sqlite3_bind_blob(sqlStmt, 3, peerZID, 12, SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmt); if (ret!=SQLITE_ROW) { sqlite3_finalize(sqlStmt); if (ret == SQLITE_DONE) {/* not found in cache, just leave cached secrets reset, but retrieve zuid, do not insert new peer ZID at this step, it must be done only when negotiation succeeds */ /* last param is NULL as we already hold the lock on database */ ret = bzrtp_cache_getZuid((void *)context->zidCache, context->selfURI, context->peerURI, context->peerZID, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &context->zuid, NULL); } else { /* we had an error querying the DB... */ ret = BZRTP_ZIDCACHE_UNABLETOREAD; } if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return ret; } /* get zuid from column 0 */ context->zuid = sqlite3_column_int(sqlStmt, 0); /* retrieve values : rs1, rs2, aux, pbx, they all are blob, columns 1,2,3,4 */ length = sqlite3_column_bytes(sqlStmt, 1); if (length>0) { /* we have rs1 */ context->cachedSecret.rs1Length = length; context->cachedSecret.rs1 = (uint8_t *)malloc(length*sizeof(uint8_t)); memcpy(context->cachedSecret.rs1, sqlite3_column_blob(sqlStmt, 1), length); } length = sqlite3_column_bytes(sqlStmt, 2); if (length>0) { /* we have rs2 */ context->cachedSecret.rs2Length = length; context->cachedSecret.rs2 = (uint8_t *)malloc(length*sizeof(uint8_t)); memcpy(context->cachedSecret.rs2, sqlite3_column_blob(sqlStmt, 2), length); } length = sqlite3_column_bytes(sqlStmt, 3); if (length>0) { /* we have aux */ context->cachedSecret.auxsecretLength = length; context->cachedSecret.auxsecret = (uint8_t *)malloc(length*sizeof(uint8_t)); memcpy(context->cachedSecret.auxsecret, sqlite3_column_blob(sqlStmt, 3), length); } length = sqlite3_column_bytes(sqlStmt, 4); if (length>0) { /* we have pbx */ context->cachedSecret.pbxsecretLength = length; context->cachedSecret.pbxsecret = (uint8_t *)malloc(length*sizeof(uint8_t)); memcpy(context->cachedSecret.pbxsecret, sqlite3_column_blob(sqlStmt, 4), length); } /* pvs is stored as blob in memory, just get the first byte(length shall be one anyway) and consider */ /* it may be NULL -> consider it 0 */ length = sqlite3_column_bytes(sqlStmt, 5); if (length!=1) { context->cachedSecret.previouslyVerifiedSas = 0; /* anything wich is not 0x01 is considered 0, so none or more than 1 byte is 0 */ } else { if (*((uint8_t *)sqlite3_column_blob(sqlStmt, 5)) == 0x01) { context->cachedSecret.previouslyVerifiedSas = 1; } else { context->cachedSecret.previouslyVerifiedSas = 0; /* anything wich is not 0x01 is considered 0 */ } } sqlite3_finalize(sqlStmt); if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return 0; } /** * @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID. * Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed and requested * Any pair ZID/sipURI shall identify an account on a device. * * @param[in/out] db the opened sqlite database pointer * @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI) * @param[in] peerURI peer URI * @param[in] peerZID peer ZID * @param[in] insertFlag A boolean managing insertion or not of a new row: * - BZRTP_ZIDCACHE_DONT_INSERT_ZUID : if not found identity binding won't lead to insertion and return zuid will be 0 * - BZRTP_ZIDCACHE_INSERT_ZUID : if not found, insert a new row in ziduri table and return newly inserted zuid * @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant * if identity binding is not found and insertFlag set to BZRTP_ZIDCACHE_DONT_INSERT_ZUID, this value is set to 0 * @param[in] zidCacheMutex Points to a mutex used to lock zidCache database access, ignored if NULL * * @return 0 on success, BZRTP_ERROR_CACHE_PEERNOTFOUND if peer was not in and the insert flag is not set to BZRTP_ZIDCACHE_INSERT_ZUID, error code otherwise */ int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], const uint8_t insertFlag, int *zuid, bctbx_mutex_t *zidCacheMutex) { char *stmt=NULL; int ret; sqlite3_stmt *sqlStmt = NULL; sqlite3 *db = (sqlite3 *)dbPointer; if (dbPointer == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } if (zidCacheMutex != NULL) { bctbx_mutex_lock(zidCacheMutex); } /* Try to fetch the requested zuid */ stmt = sqlite3_mprintf("SELECT zuid FROM ziduri WHERE selfuri=? AND peeruri=? AND zid=? ORDER BY zuid LIMIT 1;"); ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_text(sqlStmt, 1, selfURI,-1,SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 2, peerURI,-1,SQLITE_TRANSIENT); sqlite3_bind_blob(sqlStmt, 3, peerZID, 12, SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmt); if (ret!=SQLITE_ROW) { /* We didn't found this binding in the DB */ sqlite3_finalize(sqlStmt); if (ret == SQLITE_DONE) { /* query executed correctly, just our data is not there */ /* shall we insert it? */ if (insertFlag == BZRTP_ZIDCACHE_INSERT_ZUID) { uint8_t *localZID = NULL; char *errmsg=NULL; /* check that we have a self ZID matching the self URI and insert a new row */ stmt = sqlite3_mprintf("SELECT zid FROM ziduri WHERE selfuri=%Q AND peeruri='self' ORDER BY zuid LIMIT 1;",selfURI); ret = sqlite3_exec(db,stmt,callback_getSelfZID,&localZID,&errmsg); sqlite3_free(stmt); if (ret != SQLITE_OK) { sqlite3_free(errmsg); if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } if (localZID==NULL) { /* this sip URI is not in our DB, do not create an association with the peer ZID/URI binding */ if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_BADINPUTDATA; } else { /* yes we know this URI on local device, add a row in the ziduri table */ free(localZID); stmt = sqlite3_mprintf("INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);"); ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL); if (ret != SQLITE_OK) { if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_free(stmt); sqlite3_bind_blob(sqlStmt, 1, peerZID, 12, SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 2, selfURI,-1,SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 3, peerURI,-1,SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmt); if (ret!=SQLITE_DONE) { if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOUPDATE; } sqlite3_finalize(sqlStmt); /* get the zuid created */ *zuid = (int)sqlite3_last_insert_rowid(db); if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return 0; } } else { /* no found and not inserted */ *zuid = 0; if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ERROR_CACHE_PEERNOTFOUND; } } else { /* we had an error querying the DB... */ if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } } /* retrieve value in column 0 */ *zuid = sqlite3_column_int(sqlStmt, 0); sqlite3_finalize(sqlStmt); if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return 0; } /** * @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be inserted, lengths of theses values * All three arrays must be the same lenght: columnsCount * If the row isn't present in the given table, it will be inserted * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to write in the db, must already exists. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to update * @param[in] values An array of buffers containing the values to insert/update matching the order of columns array * @param[in] lengths An array of integer containing the lengths of values array buffer matching the order of columns array * @param[in] columnsCount length common to columns,values and lengths arrays * * @return 0 on succes, error code otherwise */ static int bzrtp_cache_write_impl(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { char *stmt=NULL; int ret,i,j; sqlite3_stmt *sqlStmt = NULL; sqlite3 *db = (sqlite3 *)dbPointer; char *insertColumnsString=NULL; int insertColumnsStringLength=0; if (dbPointer == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } if (zuid == 0) { /* we're giving an invalid zuid, means we were not able to retrieve it previously, just do nothing */ return BZRTP_ERROR_CACHE_PEERNOTFOUND; } /* As the zuid row may not be already present in the table, we must run an insert or update SQL command which is not provided by sqlite3 */ /* UPSERT strategy: run update first and check for changes */ /* do not perform any check on table name or columns name, the sql statement will just fail when they are wrong */ /* prepare the columns list string, use the %w formatting option as it shall protect us from anything in the column name */ for (i=0; izidCache == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } if (context->zuid == 0) { /* we're giving an invalid zuid, means we were not able to retrieve it previously, just do nothing */ return BZRTP_ERROR_CACHE_PEERNOTFOUND; } if (context->zidCacheMutex != NULL) { bctbx_mutex_lock(context->zidCacheMutex); } sqlite3_exec(context->zidCache, "BEGIN TRANSACTION;", NULL, NULL, NULL); /* Retrieve the peerUri and active flag from ziduri table */ stmt = sqlite3_mprintf("SELECT peeruri, active FROM ziduri WHERE zuid=? LIMIT 1;"); ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmt, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { sqlite3_exec(context->zidCache, "ROLLBACK;", NULL, NULL, NULL); if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_int(sqlStmt, 1, context->zuid); ret = sqlite3_step(sqlStmt); if (ret!=SQLITE_ROW) { /* We didn't found this zuid in the DB -> we would not be able to write */ sqlite3_finalize(sqlStmt); sqlite3_exec(context->zidCache, "ROLLBACK;", NULL, NULL, NULL); if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOUPDATE; } /* retrieve values 0:peeruri, 1:active */ peeruri = sqlite3_column_text(sqlStmt, 0); /* warning: finalize the statement will invalidate peeruri pointer */ activeFlag = sqlite3_column_int(sqlStmt, 1); /* if active flag is already set, just do nothing otherwise set it and reset all others with the same peeruri(active device is shared among local users) */ if (activeFlag == 0) { sqlite3_stmt *sqlStmtActive = NULL; /* reset all active flags with this peeruri */ stmt = sqlite3_mprintf("UPDATE ziduri SET active=0 WHERE active<>0 AND zuid<>? AND peeruri=?;"); ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmtActive, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { sqlite3_finalize(sqlStmt); sqlite3_finalize(sqlStmtActive); sqlite3_exec(context->zidCache, "ROLLBACK;", NULL, NULL, NULL); if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_int(sqlStmtActive, 1, context->zuid); sqlite3_bind_text(sqlStmtActive, 2, (const char *)peeruri, -1, SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmtActive); sqlite3_finalize(sqlStmtActive); /* set to 1 the active flag four current row */ stmt = sqlite3_mprintf("UPDATE ziduri SET active=1 WHERE zuid=?;"); ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmtActive, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { sqlite3_finalize(sqlStmt); sqlite3_finalize(sqlStmtActive); sqlite3_exec(context->zidCache, "ROLLBACK;", NULL, NULL, NULL); if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_int(sqlStmtActive, 1, context->zuid); ret = sqlite3_step(sqlStmtActive); sqlite3_finalize(sqlStmtActive); } sqlite3_finalize(sqlStmt); /* and perform the actual writing */ ret = bzrtp_cache_write_impl(context->zidCache, context->zuid, tableName, columns, values, lengths, columnsCount); if (ret == 0) { sqlite3_exec(context->zidCache, "COMMIT;", NULL, NULL, NULL); } else { sqlite3_exec(context->zidCache, "ROLLBACK;", NULL, NULL, NULL); } if (context->zidCacheMutex != NULL) { bctbx_mutex_unlock(context->zidCacheMutex); } return ret; } /** * @brief Read data from specified table/columns from cache adressing it by zuid (ZID/URI binding id used in cache) * Get arrays of column names, values to be read, and the number of colums to be read * Produce an array of values(uint8_t arrays) and a array of corresponding lengths * Values memory is allocated by this function and must be freed by caller * * @param[in/out] dbPointer Pointer to an already opened sqlite db * @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI) * @param[in] tableName The name of the table to read in the db. Null terminated string * @param[in] columns An array of null terminated strings containing the name of the columns to read, the array's length is columnsCount * @param[out] values An array of uint8_t pointers, each one will be allocated to the read value and they must be freed by caller * @param[out] lengths An array of integer containing the lengths of values array buffer read * @param[in] columnsCount length common to columns,values and lengths arrays * * @return 0 on succes, error code otherwise */ static int bzrtp_cache_read_impl(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { char *stmt=NULL; int ret,i,j; sqlite3_stmt *sqlStmt = NULL; sqlite3 *db = (sqlite3 *)dbPointer; char *readColumnsString=NULL; int readColumnsStringLength=0; if (dbPointer == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } /* do not perform any check on table name or columns name, the sql statement will just fail when they are wrong */ /* prepare the columns list string, use the %w formatting option as it shall protect us from anything in the column name */ for (i=0; i0) { /* we have rs1 */ lengths[i] = length; values[i] = (uint8_t *)malloc(length*sizeof(uint8_t)); memcpy(values[i], sqlite3_column_blob(sqlStmt, i), length); } else { /* data is null in DB */ values[i] = NULL; lengths[i] = 0; } } sqlite3_finalize(sqlStmt); return 0; } /* non locking database version of the previous function, is deprecated but kept for compatibility */ int bzrtp_cache_read(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { return bzrtp_cache_read_impl(dbPointer, zuid, tableName, columns, values, lengths, columnsCount); } /* locking database version of the previous function */ int bzrtp_cache_read_lock(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount, bctbx_mutex_t *zidCacheMutex) { int retval; if (dbPointer != NULL && zidCacheMutex != NULL) { bctbx_mutex_lock(zidCacheMutex); retval = bzrtp_cache_read_impl(dbPointer, zuid, tableName, columns, values, lengths, columnsCount); bctbx_mutex_unlock(zidCacheMutex); return retval; } else { return bzrtp_cache_read_impl(dbPointer, zuid, tableName, columns, values, lengths, columnsCount); } } /** * @brief Perform migration from xml version to sqlite3 version of cache * Warning: new version of cache associate a ZID to each local URI, the old one did not * the migration function will associate any data in the cache to the sip URI given in parameter which shall be the default URI * @param[in] cacheXml a pointer to an xmlDocPtr structure containing the old cache to be migrated * @param[in/out] cacheSqlite a pointer to an sqlite3 structure containing a cache initialised using bzrtp_cache_init function * @param[in] selfURI default sip URI for this end point, NULL terminated char * * @return 0 on success, BZRTP_ERROR_CACHEDISABLED when bzrtp was not compiled with cache enabled, BZRTP_ERROR_CACHEMIGRATIONFAILED on error during migration */ int bzrtp_cache_migration(void *cacheXmlPtr, void *cacheSqlite, const char *selfURI) { #ifdef HAVE_LIBXML2 if (cacheXmlPtr) { xmlDocPtr cacheXml = (xmlDocPtr)cacheXmlPtr; xmlNodePtr cur; xmlChar *selfZidHex=NULL; uint8_t selfZID[12]; sqlite3 *db = (sqlite3 *)cacheSqlite; sqlite3_stmt *sqlStmt = NULL; int ret; /* parse the cache to get the selfZID and insert it in sqlcache */ cur = xmlDocGetRootElement(cacheXml); /* if we found a root element, parse its children node */ if (cur!=NULL) { cur = cur->xmlChildrenNode; } selfZidHex = NULL; while (cur!=NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */ selfZidHex = xmlNodeListGetString(cacheXml, cur->xmlChildrenNode, 1); bctbx_str_to_uint8(selfZID, selfZidHex, 24); break; } cur = cur->next; } /* did we found a self ZID? */ if (selfZidHex == NULL) { bctbx_warning("ZRTP/LIME cache migration: Failed to parse selfZID"); return BZRTP_ERROR_CACHEMIGRATIONFAILED; } /* insert the selfZID in cache, associate it to default local sip:uri in case we have more than one */ bctbx_message("ZRTP/LIME cache migration: found selfZID %.24s link it to default URI %s in SQL cache", selfZidHex, selfURI); xmlFree(selfZidHex); ret = sqlite3_prepare_v2(db, "INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);", -1, &sqlStmt, NULL); if (ret != SQLITE_OK) { bctbx_warning("ZRTP/LIME cache migration: Failed to insert selfZID"); return BZRTP_ERROR_CACHEMIGRATIONFAILED; } sqlite3_bind_blob(sqlStmt, 1, selfZID, 12, SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 2, selfURI,-1,SQLITE_TRANSIENT); sqlite3_bind_text(sqlStmt, 3, "self",-1,SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmt); if (ret!=SQLITE_DONE) { bctbx_warning("ZRTP/LIME cache migration: Failed to insert selfZID"); return BZRTP_ERROR_CACHEMIGRATIONFAILED; } sqlite3_finalize(sqlStmt); /* loop over all the peer node in the xml cache and get from them : uri(can be more than one), ZID, rs1, rs2, pvs, sndKey, rcvKey, sndSId, rcvSId, sndIndex, rcvIndex, valid */ /* some of these may be missing(pvs, valid, rs2) but we'll consider them NULL */ /* aux and pbx secrets were not used, so don't even bother looking for them */ cur = xmlDocGetRootElement(cacheXml)->xmlChildrenNode; while (cur!=NULL) { /* loop on all peer nodes */ if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))) { /* found a peer node, check if there is a sipURI node in it (other nodes are just ignored) */ int i; xmlNodePtr peerNodeChildren = cur->xmlChildrenNode; xmlChar *nodeContent = NULL; xmlChar *peerZIDString = NULL; uint8_t peerZID[12]; uint8_t peerZIDFound=0; xmlChar *peerUri[128]; /* array to contain all the peer uris found in one node */ /* hopefully they won't be more than 128(it would mean some peer has more than 128 accounts and we called all of them...) */ int peerUriIndex=0; /* index of previous array */ const char *zrtpColNames[] = {"rs1", "rs2", "pvs"}; uint8_t *zrtpColValues[] = {NULL, NULL, NULL}; size_t zrtpColExpectedLengths[] = {32,32,1}; size_t zrtpColLengths[] = {0,0,0}; const char *limeColNames[] = {"sndKey", "rcvKey", "sndSId", "rcvSId", "sndIndex", "rcvIndex", "valid"}; uint8_t *limeColValues[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; size_t limeColExpectedLengths[] = {32,32,32,32,4,4,8}; size_t limeColLengths[] = {0,0,0,0,0,0,0}; /* check all the children nodes to retrieve all information we may get */ while (peerNodeChildren!=NULL && peerUriIndex<128) { if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"uri")) { /* found a peer an URI node, get the content */ peerUri[peerUriIndex] = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); peerUriIndex++; } if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)"ZID")) { peerZIDString = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); bctbx_str_to_uint8(peerZID, peerZIDString, 24); peerZIDFound=1; } for (i=0; i<3; i++) { if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)zrtpColNames[i])) { nodeContent = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); zrtpColValues[i] = (uint8_t *)bctbx_malloc(zrtpColExpectedLengths[i]); bctbx_str_to_uint8(zrtpColValues[i], nodeContent, 2*zrtpColExpectedLengths[i]); zrtpColLengths[i]=zrtpColExpectedLengths[i]; } } for (i=0; i<7; i++) { if (!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)limeColNames[i])) { nodeContent = xmlNodeListGetString(cacheXml, peerNodeChildren->xmlChildrenNode, 1); limeColValues[i] = (uint8_t *)bctbx_malloc(limeColExpectedLengths[i]); bctbx_str_to_uint8(limeColValues[i], nodeContent, 2*limeColExpectedLengths[i]); limeColLengths[i]=limeColExpectedLengths[i]; } } peerNodeChildren = peerNodeChildren->next; xmlFree(nodeContent); nodeContent=NULL; } if (peerUriIndex>0 && peerZIDFound==1) { /* we found at least an uri in this peer node, extract the keys all other informations */ /* retrieve all the informations */ /* loop over all the uri founds */ for (i=0; inext; } return 0; } return BZRTP_ERROR_CACHEMIGRATIONFAILED; #else /* HAVE_LIBXML2 */ bctbx_error("ZRTP/LIME cache migration: could not perform migration as LIBMXL2 is not linked to bzrtp."); return BZRTP_ERROR_CACHEMIGRATIONFAILED; #endif /* HAVE_LIBXML2 */ } /* * @brief Retrieve from bzrtp cache the trust status(based on the previously verified flag) of a peer URI * * This function will return the SAS validation status of the active device * associated to the given peerURI. * * Important note about the active device: * - any ZRTP exchange with a peer device will set it to be the active one for its sip:uri * - the concept of active device is shared between local accounts if there are several of them, it means that : * - if you have several local users on your device, each of them may have an entry in the ZRTP cache with a particular peer sip:uri (if they ever got in contact with it) but only one of this entry is set to active * - this function will return the status associated to the last updated entry without any consideration for the local users it is associated with * - any call where the SAS was neither accepted or rejected will not update the trust status but will set as active device for the peer sip:uri the one involved in the call * * This function is intended for use in a mono-device environment. * * @param[in] dbPointer Pointer to an already opened sqlite db * @param[in] peerURI The peer sip:uri we're interested in * * @return one of: * - BZRTP_CACHE_PEER_STATUS_UNKNOWN : this uri is not present in cache OR during calls with the active device, SAS never was validated or rejected * Note: once the SAS has been validated or rejected, the status will never return to UNKNOWN(unless you delete your cache) * - BZRTP_CACHE_PEER_STATUS_VALID : the active device status is set to valid * - BZRTP_CACHE_PEER_STATUS_INVALID : the active peer device status is set to invalid * */ int bzrtp_cache_getPeerStatus_lock(void *dbPointer, const char *peerURI, bctbx_mutex_t *zidCacheMutex) { char *stmt=NULL; int ret,retval = BZRTP_CACHE_PEER_STATUS_UNKNOWN; sqlite3_stmt *sqlStmt = NULL; sqlite3 *db = (sqlite3 *)dbPointer; /* initial checks */ if (dbPointer == NULL) { /* we are running cacheless */ return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; } if (zidCacheMutex != NULL) { bctbx_mutex_lock(zidCacheMutex); } /* Retrieve the pvs flag from zrtp table for the given peerURI * Order by active desc so we get the row with the active flag set to 1 * if there is no such row(just after migration from version 0.0.1 of DB schema * we will get the last device inserted for this peer, it is the most likely to be the active one in the context of a mono-device environment) */ stmt = sqlite3_mprintf("SELECT z.pvs FROM ziduri as zu INNER JOIN zrtp as z ON z.zuid=zu.zuid WHERE zu.peeruri=? ORDER BY zu.active DESC,zu.zuid DESC LIMIT 1;"); ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL); sqlite3_free(stmt); if (ret != SQLITE_OK) { if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return BZRTP_ZIDCACHE_UNABLETOREAD; } sqlite3_bind_text(sqlStmt, 1, peerURI, -1, SQLITE_TRANSIENT); ret = sqlite3_step(sqlStmt); if (ret==SQLITE_ROW) { /* We found the peerURI in the DB */ /* pvs is stored as blob in memory, just get the first byte(length shall be one anyway) */ /* it may be NULL -> return UNKNOWN */ int length = sqlite3_column_bytes(sqlStmt, 0); if (length!=1) { /* value is NULL(or we have something that is not 0x01 or 0x00) in db, we do not know the status of this device */ retval = BZRTP_CACHE_PEER_STATUS_UNKNOWN; } else { if (*((uint8_t *)sqlite3_column_blob(sqlStmt, 0)) == 0x01) { retval = BZRTP_CACHE_PEER_STATUS_VALID; } else { retval = BZRTP_CACHE_PEER_STATUS_INVALID; } } } else { /* the peerURI was not found in DB */ /* Note: if ret != SQLITE_DONE, we had an error when accessing the database, * we just swallow it and return unknown status but signal it in traces */ if (ret != SQLITE_DONE) { bctbx_warning("Querying DB for peer(%s) status returned an sqlite error code %d\n", peerURI, ret); } retval = BZRTP_CACHE_PEER_STATUS_UNKNOWN; } sqlite3_finalize(sqlStmt); if (zidCacheMutex != NULL) { bctbx_mutex_unlock(zidCacheMutex); } return retval; } #else /* ZIDCACHE_ENABLED */ static int bzrtp_getSelfZID_impl(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext) { /* we are running cacheless, return a random number */ if (RNGContext != NULL) { bctbx_rng_get(RNGContext, selfZID, 12); } else { return BZRTP_CACHE_DATA_NOTFOUND; } return 0; } int bzrtp_getSelfZID(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext) { return bzrtp_getSelfZID_impl(dbPointer, selfURI, selfZID, RNGContext); } int bzrtp_getSelfZID_lock(void *dbPointer, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext, bctbx_mutex_t *zidCacheMutex) { return bzrtp_getSelfZID_impl(dbPointer, selfURI, selfZID, RNGContext); } int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]) { if (context == NULL) { return BZRTP_ZIDCACHE_INVALID_CONTEXT; } /* resert cached secret buffer */ free(context->cachedSecret.rs1); free(context->cachedSecret.rs2); free(context->cachedSecret.pbxsecret); free(context->cachedSecret.auxsecret); context->cachedSecret.rs1 = NULL; context->cachedSecret.rs1Length = 0; context->cachedSecret.rs2 = NULL; context->cachedSecret.rs2Length = 0; context->cachedSecret.pbxsecret = NULL; context->cachedSecret.pbxsecretLength = 0; context->cachedSecret.auxsecret = NULL; context->cachedSecret.auxsecretLength = 0; context->cachedSecret.previouslyVerifiedSas = 0; return 0; } int bzrtp_cache_write_active(bzrtpContext_t *context, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_write(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_write_lock(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount, bctbx_mutex_t *zidCacheMutex) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_read(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_read_lock(void *dbPointer, int zuid, const char *tableName, const char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount, bctbx_mutex_t *zidCacheMutex) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_migration(void *cacheXml, void *cacheSqlite, const char *selfURI) { return BZRTP_ERROR_CACHEDISABLED; } int bzrtp_cache_getPeerStatus_lock(void *dbPointer, const char *peerURI, bctbx_mutex_t *zidCacheMutex) { return BZRTP_CACHE_PEER_STATUS_UNKNOWN; } int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], const uint8_t insertFlag, int *zuid, bctbx_mutex_t *zidCacheMutex) { return BZRTP_ERROR_CACHEDISABLED; } #endif /* ZIDCACHE_ENABLED */ bzrtp-4.4.13/test/000077500000000000000000000000001364144501400137255ustar00rootroot00000000000000bzrtp-4.4.13/test/CMakeLists.txt000066400000000000000000000044351364144501400164730ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2014 Belledonne Communications, Grenoble France # ############################################################################ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ set(TEST_SOURCES bzrtpCryptoTest.c bzrtpParserTest.c bzrtpConfigsTest.c bzrtpZidCacheTest.c bzrtpTest.c testUtils.c ) add_definitions( -DBCTBX_LOG_DOMAIN="bzrtp" ) bc_apply_compile_flags(TEST_SOURCES STRICT_OPTIONS_CPP) add_executable(bzrtpTest ${TEST_SOURCES}) set_target_properties(bzrtpTest PROPERTIES LINK_FLAGS "${LINK_FLAGS}") set_target_properties(bzrtpTest PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(bzrtpTest PRIVATE bctoolbox bctoolbox-tester) if(ENABLE_STATIC) target_link_libraries(bzrtpTest PRIVATE bzrtp) else() target_link_libraries(bzrtpTest PRIVATE bzrtp) endif() if(SQLITE3_FOUND) target_include_directories(bzrtpTest PRIVATE ${SQLITE3_INCLUDE_DIRS}) target_link_libraries(bzrtpTest PRIVATE ${SQLITE3_LIBRARIES}) endif() if(XML2_FOUND) target_include_directories(bzrtpTest PRIVATE ${XML2_INCLUDE_DIRS}) target_link_libraries(bzrtpTest PRIVATE ${XML2_LIBRARIES}) endif() if(HAVE_SQRT) target_link_libraries(bzrtpTest PRIVATE m) endif() unset(PATTERN_FILES_IN_TEST_DIR CACHE) find_file(PATTERN_FILES_IN_TEST_DIR patternZIDAlice.sqlite ${CMAKE_CURRENT_BINARY_DIR} ) if (NOT PATTERN_FILES_IN_TEST_DIR) file(COPY patternZIDAlice.sqlite DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) endif() add_test(NAME bzrtpTest COMMAND bzrtpTest --resource-dir .) bzrtp-4.4.13/test/Makefile.am000066400000000000000000000007711364144501400157660ustar00rootroot00000000000000if ENABLE_TESTS noinst_PROGRAMS=bzrtpTest bzrtpTest_SOURCES=bzrtpTest.c \ testUtils.c testUtils.h \ bzrtpCryptoTest.c bzrtpCryptoTest.h \ bzrtpParserTest.c bzrtpParserTest.h\ bzrtpConfigsTest.c \ bzrtpZidCacheTest.c bzrtpTest_CFLAGS=$(BCTOOLBOXTESTER_CFLAGS) $(LIBXML2_CFLAGS) bzrtpTest_LDADD=$(top_builddir)/src/libbzrtp.la $(BCTOOLBOX_LIBS) $(BCTOOLBOXTESTER_LIBS) $(SQLITE3_LIBS) $(LIBXML2_LIBS) -lm AM_CPPFLAGS=-I$(top_srcdir)/include test: bzrtpTest ./bzrtpTest endif bzrtp-4.4.13/test/bzrtpConfigsTest.c000066400000000000000000002405511364144501400174120ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "bzrtp/bzrtp.h" #include "zidCache.h" #include "bzrtpTest.h" #include "testUtils.h" #define MAX_PACKET_LENGTH 1000 #define MAX_QUEUE_SIZE 10 #define MAX_CRYPTO_ALG 10 #define MAX_NUM_CHANNEL ZRTP_MAX_CHANNEL_NUMBER typedef struct packetDatas_struct { uint8_t packetString[MAX_PACKET_LENGTH]; uint16_t packetLength; } packetDatas_t; typedef struct clientContext_struct { uint8_t id; bzrtpContext_t *bzrtpContext; bctbx_mutex_t zidCacheMutex; bzrtpSrtpSecrets_t *secrets; int32_t pvs; uint8_t haveCacheMismatch; uint8_t sendExportedKey[16]; uint8_t recvExportedKey[16]; } clientContext_t; typedef struct cryptoParams_struct { uint8_t cipher[MAX_CRYPTO_ALG] ; uint8_t cipherNb; uint8_t hash[MAX_CRYPTO_ALG] ; uint8_t hashNb; uint8_t keyAgreement[MAX_CRYPTO_ALG] ; uint8_t keyAgreementNb; uint8_t sas[MAX_CRYPTO_ALG] ; uint8_t sasNb; uint8_t authtag[MAX_CRYPTO_ALG] ; uint8_t authtagNb; uint8_t dontValidateSASflag; /**< if set to 1, SAS will not be validated even if matching peer **/ /**< if set to 2, SAS will be reset even if matching peer **/ } cryptoParams_t; /* Global vars: message queues for Alice and Bob */ static packetDatas_t aliceQueue[MAX_QUEUE_SIZE]; static packetDatas_t bobQueue[MAX_QUEUE_SIZE]; static uint8_t aliceQueueIndex = 0; static uint8_t bobQueueIndex = 0; /* have ids to represent Alice and Bob */ #define ALICE 0x1 #define BOB 0x2 #define ALICE_SSRC_BASE 0x12345000 #define BOB_SSRC_BASE 0x87654000 static cryptoParams_t withoutX255 = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}; static cryptoParams_t withX255 = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}; cryptoParams_t *defaultCryptoAlgoSelection(void) { if (bctbx_key_agreement_algo_list()&BCTBX_ECDH_X25519) { return &withX255; } return &withoutX255; } static cryptoParams_t withoutX255noSAS = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,1}; static cryptoParams_t withX255noSAS = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,1}; cryptoParams_t *defaultCryptoAlgoSelectionNoSASValidation(void) { if (bctbx_key_agreement_algo_list()&BCTBX_ECDH_X25519) { return &withX255noSAS; } return &withoutX255noSAS; } static cryptoParams_t withoutX255resetSAS = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,2}; static cryptoParams_t withX255resetSAS = {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,2}; cryptoParams_t *defaultCryptoAlgoSelectionResetSAS(void) { if (bctbx_key_agreement_algo_list()&BCTBX_ECDH_X25519) { return &withX255resetSAS; } return &withoutX255resetSAS; } /* static global settings and their reset function */ static uint64_t msSTC = 0; /* Simulation Time Coordinated start at 0 and increment it at each sleep, is in milliseconds */ static int loosePacketPercentage=0; /* simulate bd network condition: loose packet */ static uint64_t timeOutLimit=1000; /* in ms, time span given to perform the ZRTP exchange */ static float fadingLostAlice=0.0; /* try not to throw away too many packet in a row */ static float fadingLostBob=0.0; /* try not to throw away too many packet in a row */ static int totalPacketLost=0; /* set a limit to the total number of packet we can loose to enforce completion of the exchange */ /* when timeout is set to this specific value, negotiation is aborted but silently fails */ #define ABORT_NEGOTIATION_TIMEOUT 24 static void resetGlobalParams() { msSTC=0; totalPacketLost =0; loosePacketPercentage=0; timeOutLimit = 1000; fadingLostBob=0; fadingLostAlice=0; } /* time functions, we do not run a real time scenario, go for fast test instead */ static uint64_t getSimulatedTime() { return msSTC; } static void STC_sleep(int ms){ msSTC +=ms; } /* routing messages */ static int sendData(void *clientData, const uint8_t *packetString, uint16_t packetLength) { /* get the client context */ clientContext_t *clientContext = (clientContext_t *)clientData; /* manage loosy network simulation */ if (loosePacketPercentage > 0) { /* make sure we cannot loose 10 packets in a row from the same sender */ if ((totalPacketLost<10) && ((float)((rand()%100 )) < loosePacketPercentage-((clientContext->id == ALICE)?fadingLostAlice:fadingLostBob))) { /* randomly discard packets */ //bzrtp_message("%d Loose %.8s from %s - LC %d\n", msSTC, packetString+16, (clientContext->id==ALICE?"Alice":"Bob"), totalPacketLost); if (clientContext->id == ALICE) { fadingLostAlice +=loosePacketPercentage/8; } else { fadingLostBob +=loosePacketPercentage/8; } return 0; } //bzrtp_message("%d Keep %.8s from %s - LC %d\n", msSTC, packetString+16, (clientContext->id==ALICE?"Alice":"Bob"), totalPacketLost); } //bzrtp_message("%ld %.8s from %s\n", msSTC, packetString+16, (clientContext->id==ALICE?"Alice":"Bob")); /* put the message in the message correct queue */ if (clientContext->id == ALICE) { /* message sent by Alice, so put it in Bob's queue */ fadingLostAlice = MAX(0,fadingLostAlice-loosePacketPercentage/2); memcpy(bobQueue[bobQueueIndex].packetString, packetString, packetLength); bobQueue[bobQueueIndex++].packetLength = packetLength; } else { /* Bob sent the message, put it in Alice's queue */ fadingLostBob = MAX(0,fadingLostBob-loosePacketPercentage/2); memcpy(bobQueue[bobQueueIndex].packetString, packetString, packetLength); memcpy(aliceQueue[aliceQueueIndex].packetString, packetString, packetLength); aliceQueue[aliceQueueIndex++].packetLength = packetLength; } return 0; } /* get SAS and SRTP keys */ int getSAS(void *clientData, bzrtpSrtpSecrets_t *secrets, int32_t pvs) { /* get the client context */ clientContext_t *clientContext = (clientContext_t *)clientData; /* store the secret struct */ clientContext->secrets = secrets; /* and the PVS flag */ clientContext->pvs = pvs; return 0; } int getMessage(void *clientData, const uint8_t level, const uint8_t message, const char *messageString) { /* get the client context */ clientContext_t *clientContext = (clientContext_t *)clientData; if (level == BZRTP_MESSAGE_ERROR && message == BZRTP_MESSAGE_CACHEMISMATCH) { clientContext->haveCacheMismatch = 1; } return 0; } int computeExportedKeys(void *clientData, int zuid, uint8_t role) { size_t keyLength = 16; /* get the client context */ clientContext_t *clientContext = (clientContext_t *)clientData; /* compute 2 exported keys with label initiator and responder */ BC_ASSERT_EQUAL(bzrtp_exportKey(clientContext->bzrtpContext, ((role==BZRTP_ROLE_RESPONDER)?"ResponderKey":"InitiatorKey"), 12, clientContext->sendExportedKey, &keyLength), 0, int, "%x"); BC_ASSERT_EQUAL(keyLength, 16, int, "%d"); /* any hash available in the config shall be able to produce a 16 bytes key */ keyLength = 16; BC_ASSERT_EQUAL(bzrtp_exportKey(clientContext->bzrtpContext, ((role==BZRTP_ROLE_INITIATOR)?"ResponderKey":"InitiatorKey"), 12, clientContext->recvExportedKey, &keyLength), 0, int, "%x"); BC_ASSERT_EQUAL(keyLength, 16, int, "%d"); /* any hash available in the config shall be able to produce a 16 bytes key */ return 0; } static int setUpClientContext(clientContext_t *clientContext, uint8_t clientID, uint32_t SSRC, void *zidCache, bctbx_mutex_t *zidCacheMutex, char *selfURI, char *peerURI, cryptoParams_t *cryptoParams) { int retval; bzrtpCallbacks_t cbs={0} ; /* set Id */ clientContext->id = clientID; clientContext->pvs=0; clientContext->haveCacheMismatch=0; /* create zrtp context */ clientContext->bzrtpContext = bzrtp_createBzrtpContext(); if (clientContext->bzrtpContext==NULL) { bzrtp_message("ERROR: can't create bzrtp context, id client is %d", clientID); return -1; } /* check cache */ if (zidCache != NULL) { #ifdef ZIDCACHE_ENABLED if (zidCacheMutex == NULL) { zidCacheMutex = &(clientContext->zidCacheMutex); bctbx_mutex_init(zidCacheMutex, NULL); } retval = bzrtp_setZIDCache_lock(clientContext->bzrtpContext, zidCache, selfURI, peerURI, zidCacheMutex); if (retval != 0 && retval != BZRTP_CACHE_SETUP) { /* return value is BZRTP_CACHE_SETUP if the cache is populated by this call */ bzrtp_message("ERROR: bzrtp_setZIDCache %0x, client id is %d\n", retval, clientID); return -2; } #else bzrtp_message("ERROR: asking for cache but not enabled at compile time\n"); return -2; #endif } /* assign callbacks */ cbs.bzrtp_sendData=sendData; cbs.bzrtp_startSrtpSession=(int (*)(void *,const bzrtpSrtpSecrets_t *,int32_t) )getSAS; cbs.bzrtp_statusMessage=getMessage; cbs.bzrtp_messageLevel = BZRTP_MESSAGE_ERROR; cbs.bzrtp_contextReadyForExportedKeys = computeExportedKeys; if ((retval = bzrtp_setCallbacks(clientContext->bzrtpContext, &cbs))!=0) { bzrtp_message("ERROR: bzrtp_setCallbacks returned %0x, client id is %d\n", retval, clientID); return -3; } /* set crypto params */ if (cryptoParams != NULL) { bzrtp_setSupportedCryptoTypes(clientContext->bzrtpContext, ZRTP_HASH_TYPE, cryptoParams->hash, cryptoParams->hashNb); bzrtp_setSupportedCryptoTypes(clientContext->bzrtpContext, ZRTP_CIPHERBLOCK_TYPE, cryptoParams->cipher, cryptoParams->cipherNb); bzrtp_setSupportedCryptoTypes(clientContext->bzrtpContext, ZRTP_KEYAGREEMENT_TYPE, cryptoParams->keyAgreement, cryptoParams->keyAgreementNb); bzrtp_setSupportedCryptoTypes(clientContext->bzrtpContext, ZRTP_AUTHTAG_TYPE, cryptoParams->authtag, cryptoParams->authtagNb); bzrtp_setSupportedCryptoTypes(clientContext->bzrtpContext, ZRTP_SAS_TYPE, cryptoParams->sas, cryptoParams->sasNb); } /* init the first channel */ bzrtp_initBzrtpContext(clientContext->bzrtpContext, SSRC); if ((retval = bzrtp_setClientData(clientContext->bzrtpContext, SSRC, (void *)clientContext))!=0) { bzrtp_message("ERROR: bzrtp_setClientData returned %0x, client id is %d\n", retval, clientID); return -4; } return 0; } static int addChannel(clientContext_t *clientContext, uint32_t SSRC) { int retval=0; /* add channel */ if ((retval = bzrtp_addChannel(clientContext->bzrtpContext, SSRC))!=0) { bzrtp_message("ERROR: bzrtp_addChannel returned %0x, client id is %d\n", retval, clientContext->id); return -1; } /* associated client data(give the same than for first channel) */ if ((retval = bzrtp_setClientData(clientContext->bzrtpContext, SSRC, (void *)clientContext))!=0) { bzrtp_message("ERROR: bzrtp_setClientData on secondary channel returned %0x, client id is %d\n", retval, clientContext->id); return -2; } /* start the channel */ if ((retval = bzrtp_startChannelEngine(clientContext->bzrtpContext, SSRC))!=0) { bzrtp_message("ERROR: bzrtp_startChannelEngine on secondary channel returned %0x, client id is %d SSRC is %d\n", retval, clientContext->id,SSRC); return -3; } return 0; } /* are all a and b field the same? Check Sas(optionnaly as it is not provided for secondary channel) and srtp keys and choosen algo*/ static int compareSecrets(bzrtpSrtpSecrets_t *a, bzrtpSrtpSecrets_t* b, uint8_t mainChannel) { if (mainChannel==TRUE) { if (strcmp(a->sas,b->sas)!=0) { return -1; } } if (mainChannel == TRUE) { if ((a->authTagAlgo!=b->authTagAlgo) || a->hashAlgo!=b->hashAlgo || a->keyAgreementAlgo!=b->keyAgreementAlgo || a->sasAlgo!=b->sasAlgo || a->cipherAlgo!=b->cipherAlgo) { return -2; } } else { if ((a->authTagAlgo!=b->authTagAlgo) || a->hashAlgo!=b->hashAlgo || a->keyAgreementAlgo!=b->keyAgreementAlgo || a->cipherAlgo!=b->cipherAlgo) { return -2; } } if (a->selfSrtpKeyLength==0 || b->selfSrtpKeyLength==0 || a->selfSrtpSaltLength==0 || b->selfSrtpSaltLength==0 || a->peerSrtpKeyLength==0 || b->peerSrtpKeyLength==0 || a->peerSrtpSaltLength==0 || b->peerSrtpSaltLength==0) { return -3; } if (a->selfSrtpKeyLength != b->peerSrtpKeyLength || a->selfSrtpSaltLength != b->peerSrtpSaltLength || a->peerSrtpKeyLength != b->selfSrtpKeyLength || a->peerSrtpSaltLength != b->selfSrtpSaltLength) { return -4; } if (memcmp (a->selfSrtpKey, b->peerSrtpKey, b->peerSrtpKeyLength) != 0 || memcmp (a->selfSrtpSalt, b->peerSrtpSalt, b->peerSrtpSaltLength) != 0 || memcmp (a->peerSrtpKey, b->selfSrtpKey, b->selfSrtpKeyLength) != 0 || memcmp (a->peerSrtpSalt, b->selfSrtpSalt, b->selfSrtpSaltLength) != 0) { return -5; } return 0; } /* compare algo sets */ static int compareAlgoList(bzrtpSrtpSecrets_t *secrets, cryptoParams_t *cryptoParams) { if (secrets->authTagAlgo != cryptoParams->authtag[0]) return -1; if (secrets->hashAlgo != cryptoParams->hash[0]) return -2; if (secrets->cipherAlgo != cryptoParams->cipher[0]) return -3; if (secrets->keyAgreementAlgo != cryptoParams->keyAgreement[0]) return -4; if (secrets->sasAlgo != cryptoParams->sas[0]) return -5; return 0; } /* defines return values bit flags(on 16 bits, use 32 to return status for Bob(16 MSB) and Alice(16 LSB)) */ #define RET_CACHE_MISMATCH 0x0001 /* * Never call directly this function in tests, its purpose is to have a flexible API according to future needs * use a variant or create a new one, see after this function */ uint32_t multichannel_exchange_full_params(cryptoParams_t *aliceCryptoParams, cryptoParams_t *bobCryptoParams, cryptoParams_t *expectedCryptoParams, void *aliceCache, bctbx_mutex_t *aliceCacheMutex, char *aliceURI, void *bobCache, bctbx_mutex_t *bobCacheMutex, char *bobURI, uint8_t checkPVS, uint8_t expectedAlicePVS, uint8_t expectedBobPVS) { int retval,channelNumber; clientContext_t Alice,Bob; uint64_t initialTime=0; uint64_t lastPacketSentTime=0; uint32_t aliceSSRC = ALICE_SSRC_BASE; uint32_t bobSSRC = BOB_SSRC_BASE; uint32_t ret=0; /*** Create the main channel */ if ((retval=setUpClientContext(&Alice, ALICE, aliceSSRC, aliceCache, aliceCacheMutex, aliceURI, bobURI, aliceCryptoParams))!=0) { bzrtp_message("ERROR: can't init setup client context id %d\n", ALICE); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return retval; } if ((retval=setUpClientContext(&Bob, BOB, bobSSRC, bobCache, bobCacheMutex, bobURI, aliceURI, bobCryptoParams))!=0) { bzrtp_message("ERROR: can't init setup client context id %d\n", BOB); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return retval; } /* start the ZRTP engine(it will send a hello packet )*/ if ((retval = bzrtp_startChannelEngine(Alice.bzrtpContext, aliceSSRC))!=0) { bzrtp_message("ERROR: bzrtp_startChannelEngine returned %0x, client id is %d SSRC is %d\n", retval, ALICE, aliceSSRC); return retval; } if ((retval = bzrtp_startChannelEngine(Bob.bzrtpContext, bobSSRC))!=0) { bzrtp_message("ERROR: bzrtp_startChannelEngine returned %0x, client id is %d SSRC is %d\n", retval, BOB, bobSSRC); return retval; } initialTime = getSimulatedTime(); while ((bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC)!= BZRTP_CHANNEL_SECURE || bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC)!= BZRTP_CHANNEL_SECURE) && (getSimulatedTime()-initialTime 1250 ) { /*higher re-emission timeout is 1200ms */ retval = bzrtp_resetRetransmissionTimer(Alice.bzrtpContext, aliceSSRC); retval +=bzrtp_resetRetransmissionTimer(Bob.bzrtpContext, bobSSRC); lastPacketSentTime=getSimulatedTime(); } } /* when timeOutLimit is set to this specific value, our intention is to start a negotiation but not to finish it, so just return without errors */ if (timeOutLimit == ABORT_NEGOTIATION_TIMEOUT) { /*** Destroy Contexts ***/ if (aliceCache != NULL && aliceCacheMutex == NULL) { /* mutex was not provided externally, so we set up ours, destroy it */ bctbx_mutex_destroy(&(Alice.zidCacheMutex)); } while (bzrtp_destroyBzrtpContext(Alice.bzrtpContext, aliceSSRC)>0 && aliceSSRC>=ALICE_SSRC_BASE) { aliceSSRC--; } if (bobCache != NULL && bobCacheMutex == NULL) { /* mutex was not provided externally, so we set up ours, destroy it */ bctbx_mutex_destroy(&(Bob.zidCacheMutex)); } while (bzrtp_destroyBzrtpContext(Bob.bzrtpContext, bobSSRC)>0 && bobSSRC>=BOB_SSRC_BASE) { bobSSRC--; } return ret; } if ((retval=bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Alice on channel1 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return retval; } if ((retval=bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Bob on channel1 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return retval; } bzrtp_message("ZRTP algo used during negotiation: Cipher: %s - KeyAgreement: %s - Hash: %s - AuthTag: %s - Sas Rendering: %s\n", bzrtp_cipher_toString(Alice.secrets->cipherAlgo), bzrtp_keyAgreement_toString(Alice.secrets->keyAgreementAlgo), bzrtp_hash_toString(Alice.secrets->hashAlgo), bzrtp_authtag_toString(Alice.secrets->authTagAlgo), bzrtp_sas_toString(Alice.secrets->sasAlgo)); if ((retval=compareSecrets(Alice.secrets, Bob.secrets, TRUE))!=0) { BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (aliceCache != NULL && bobCache != NULL) { bzrtp_resetSASVerified(Alice.bzrtpContext); bzrtp_resetSASVerified(Bob.bzrtpContext); } return retval; } else { /* SAS comparison is Ok, if we have a cache, confirm it */ if (aliceCache != NULL && bobCache != NULL) { /* Confirm only when the cryptoParam->dontValidateSASflag is not present or set to 0 */ if (aliceCryptoParams==NULL || aliceCryptoParams->dontValidateSASflag == 0) { bzrtp_SASVerified(Alice.bzrtpContext); } else if (aliceCryptoParams!=NULL && aliceCryptoParams->dontValidateSASflag == 2) { /* if flag is set to 2 reset the SAS */ bzrtp_resetSASVerified(Alice.bzrtpContext); } /* Confirm only when the cryptoParam->dontValidateSASflag is not present or set to 0 */ if (bobCryptoParams==NULL || bobCryptoParams->dontValidateSASflag == 0) { bzrtp_SASVerified(Bob.bzrtpContext); } else if (bobCryptoParams!=NULL && bobCryptoParams->dontValidateSASflag == 2) { /* if flag is set to 2 reset the SAS */ bzrtp_resetSASVerified(Bob.bzrtpContext); } /* if flag is set to 1 just ignore the SAS validation */ } } /* shall we check the PVS returned by the SAS callback? */ if (checkPVS==TRUE) { BC_ASSERT_EQUAL(Alice.pvs, expectedAlicePVS, int, "%d"); BC_ASSERT_EQUAL(Bob.pvs, expectedBobPVS, int, "%d"); } /* if we have expected crypto param, check our result */ if (expectedCryptoParams!=NULL) { BC_ASSERT_EQUAL(compareAlgoList(Alice.secrets,expectedCryptoParams), 0, int, "%d"); } /* check exported keys */ BC_ASSERT_EQUAL(memcmp(Alice.sendExportedKey, Bob.recvExportedKey, 16), 0, int, "%d"); BC_ASSERT_EQUAL(memcmp(Alice.recvExportedKey, Bob.sendExportedKey, 16), 0, int, "%d"); /* open as much channels as we can */ for (channelNumber=2; channelNumber<=MAX_NUM_CHANNEL; channelNumber++) { /* increase SSRCs as they are used to identify a channel */ aliceSSRC++; bobSSRC++; /* start a new channel */ if ((retval=addChannel(&Alice, aliceSSRC))!=0) { bzrtp_message("ERROR: can't add a second channel to client context id %d\n", ALICE); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return retval; } if ((retval=addChannel(&Bob, bobSSRC))!=0) { bzrtp_message("ERROR: can't add a second channel to client context id %d\n", ALICE); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return retval; } initialTime = getSimulatedTime(); while ((bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC)!= BZRTP_CHANNEL_SECURE || bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC)!= BZRTP_CHANNEL_SECURE) && (getSimulatedTime()-initialTime 1250 ) { /*higher re-emission timeout is 1200ms */ retval = bzrtp_resetRetransmissionTimer(Alice.bzrtpContext, aliceSSRC); retval += bzrtp_resetRetransmissionTimer(Bob.bzrtpContext, bobSSRC); lastPacketSentTime=getSimulatedTime(); } } if ((retval=bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Alice on channel2 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return retval; } if ((retval=bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Bob on channel2 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return retval; } bzrtp_message("Channel %d :ZRTP algo used during negotiation: Cipher: %s - KeyAgreement: %s - Hash: %s - AuthTag: %s - Sas Rendering: %s\n", channelNumber, bzrtp_cipher_toString(Alice.secrets->cipherAlgo), bzrtp_keyAgreement_toString(Alice.secrets->keyAgreementAlgo), bzrtp_hash_toString(Alice.secrets->hashAlgo), bzrtp_authtag_toString(Alice.secrets->authTagAlgo), bzrtp_sas_toString(Alice.secrets->sasAlgo)); if ((retval=compareSecrets(Alice.secrets, Bob.secrets, FALSE))!=0) { BC_ASSERT_EQUAL(retval, 0, int, "%d"); } } /*** Destroy Contexts ***/ if (aliceCache != NULL && aliceCacheMutex == NULL) { /* mutex was not provided externally, so we set up ours, destroy it */ bctbx_mutex_destroy(&(Alice.zidCacheMutex)); } while (bzrtp_destroyBzrtpContext(Alice.bzrtpContext, aliceSSRC)>0 && aliceSSRC>=ALICE_SSRC_BASE) { aliceSSRC--; } if (bobCache != NULL && bobCacheMutex == NULL) { /* mutex was not provided externally, so we set up ours, destroy it */ bctbx_mutex_destroy(&(Bob.zidCacheMutex)); } while (bzrtp_destroyBzrtpContext(Bob.bzrtpContext, bobSSRC)>0 && bobSSRC>=BOB_SSRC_BASE) { bobSSRC--; } /** Compute return value **/ if (Alice.haveCacheMismatch==1) { ret |= RET_CACHE_MISMATCH; } if (Bob.haveCacheMismatch==1) { ret |= RET_CACHE_MISMATCH<<16; } return ret; } /* Variants of the exchange function with less parameter : never call directly the full params one but use one of these */ uint32_t multichannel_exchange_pvs_params(cryptoParams_t *aliceCryptoParams, cryptoParams_t *bobCryptoParams, cryptoParams_t *expectedCryptoParams, void *aliceCache, char *aliceURI, void *bobCache, char *bobURI, uint8_t checkPVS, uint8_t expectedAlicePVS, uint8_t expectedBobPVS) { return multichannel_exchange_full_params(aliceCryptoParams, bobCryptoParams, expectedCryptoParams, aliceCache, NULL, aliceURI, bobCache, NULL, bobURI, checkPVS, expectedAlicePVS, expectedBobPVS); } uint32_t multichannel_exchange(cryptoParams_t *aliceCryptoParams, cryptoParams_t *bobCryptoParams, cryptoParams_t *expectedCryptoParams, void *aliceCache, char *aliceURI, void *bobCache, char *bobURI) { return multichannel_exchange_full_params(aliceCryptoParams, bobCryptoParams, expectedCryptoParams, aliceCache, NULL, aliceURI, bobCache, NULL, bobURI, FALSE, 0, 0); } uint32_t multichannel_exchange_mutex(cryptoParams_t *aliceCryptoParams, cryptoParams_t *bobCryptoParams, cryptoParams_t *expectedCryptoParams, void *aliceCache, bctbx_mutex_t *aliceCacheMutex, char *aliceURI, void *bobCache, bctbx_mutex_t *bobCacheMutex, char *bobURI) { return multichannel_exchange_full_params(aliceCryptoParams, bobCryptoParams, expectedCryptoParams, aliceCache, aliceCacheMutex, aliceURI, bobCache, bobCacheMutex, bobURI, FALSE, 0, 0); } static void test_cacheless_exchange(void) { cryptoParams_t *pattern; /* Reset Global Static settings */ resetGlobalParams(); /* Note: common algo selection is not tested here(this is done in some cryptoUtils tests) here we just perform an exchange with any final configuration avalaible and check it goes well */ cryptoParams_t patterns[] = { {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH3k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_DH2k},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{0},0,{0},0,{0},0,{0},0,{0},0,0}, /* this pattern will end the run because cipher nb is 0 */ }; /* serie tested only if ECDH is available */ cryptoParams_t ecdh_patterns[] = { {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES1},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X448},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S256},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B32},1,{ZRTP_AUTHTAG_HS80},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS32},1,0}, {{ZRTP_CIPHER_AES3},1,{ZRTP_HASH_S384},1,{ZRTP_KEYAGREEMENT_X255},1,{ZRTP_SAS_B256},1,{ZRTP_AUTHTAG_HS80},1,0}, {{0},0,{0},0,{0},0,{0},0,{0},0,0}, /* this pattern will end the run because cipher nb is 0 */ }; pattern = &patterns[0]; /* pattern is a pointer to current pattern */ while (pattern->cipherNb!=0) { BC_ASSERT_EQUAL(multichannel_exchange(pattern, pattern, pattern, NULL, NULL, NULL, NULL), 0, int, "%x"); pattern++; /* point to next row in the array of patterns */ } /* with ECDH agreement types if available */ if (bctbx_key_agreement_algo_list()&BCTBX_ECDH_X25519) { pattern = &ecdh_patterns[0]; /* pattern is a pointer to current pattern */ while (pattern->cipherNb!=0) { BC_ASSERT_EQUAL(multichannel_exchange(pattern, pattern, pattern, NULL, NULL, NULL, NULL), 0, int, "%x"); pattern++; /* point to next row in the array of patterns */ } } BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), NULL, NULL, NULL, NULL), 0, int, "%x"); } static void test_loosy_network(void) { int i,j; resetGlobalParams(); srand((unsigned int)time(NULL)); /* run through all the configs 10 times to maximise chance to spot a random error based on a specific packet lost sequence */ for (j=0; j<10; j++) { for (i=1; i<60; i+=1) { resetGlobalParams(); timeOutLimit =100000; //outrageous time limit just to be sure to complete, not run in real time anyway loosePacketPercentage=i; BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), NULL, NULL, NULL, NULL), 0, int, "%x"); } } } static void test_cache_enabled_exchange(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bobDB=NULL; uint8_t selfZIDalice[12]; uint8_t selfZIDbob[12]; int zuidAlice=0,zuidBob=0; const char *colNames[] = {"rs1", "rs2", "pvs"}; uint8_t *colValuesAlice[3]; size_t colLengthAlice[3]; uint8_t *colValuesBob[3]; size_t colLengthBob[3]; int i; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_simpleCache.sqlite"); char *bobTesterFile = bc_tester_file("tmpZIDBob_simpleCache.sqlite"); resetGlobalParams(); /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bobTesterFile); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(bobTesterFile, &bobDB); /* make a first exchange */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), 0, int, "%x"); /* after the first exchange we shall have both pvs values at 1 and both rs1 identical and rs2 null, retrieve them from cache and check it */ /* first get each ZIDs, note give NULL as RNG context may lead to segfault in case of error(caches were not created correctly)*/ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)aliceDB, "alice@sip.linphone.org", selfZIDalice, NULL, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)bobDB, "bob@sip.linphone.org", selfZIDbob, NULL, NULL), 0, int, "%x"); /* then get the matching zuid in cache */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", selfZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidAlice, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)bobDB, "bob@sip.linphone.org", "alice@sip.linphone.org", selfZIDalice, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidBob, NULL), 0, int, "%x"); /* retrieve the values */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); /* and compare to expected */ /* rs1 is set and they are both the same */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); /* rs2 is unset(NULL) */ BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); /* pvs is equal to 1 */ BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); colValuesAlice[i]=NULL; } /* make a second exchange */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), 0, int, "%x"); /* read new values in cache, ZIDs and zuids must be identical, read alice first to be able to check rs2 with old rs1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); /* check what is now rs2 is the old rs1 */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[0], 32), 0, int, "%d"); /* colValuesBob, still old values from before the second exchange */ /* free buffers */ for (i=0; i<3; i++) { free(colValuesBob[i]); colValuesBob[i]=NULL; } /* so read bob updated values and compare rs1, rs2 and check pvs is still at 1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[1], 32), 0, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); free(colValuesBob[i]); } sqlite3_close(aliceDB); sqlite3_close(bobDB); /* clean temporary files */ remove(aliceTesterFile); remove(bobTesterFile); bc_free(aliceTesterFile); bc_free(bobTesterFile); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } /* first perform an exchange to establish a correct shared cache, then modify one of them and perform an other exchange to check we have a cache mismatch warning */ static void test_cache_mismatch_exchange(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bobDB=NULL; uint8_t selfZIDalice[12]; uint8_t selfZIDbob[12]; int zuidAlice=0,zuidBob=0; const char *colNames[] = {"rs1", "rs2", "pvs"}; uint8_t *colValuesAlice[3]; size_t colLengthAlice[3]; uint8_t *colValuesBob[3]; size_t colLengthBob[3]; int i; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_cacheMismatch.sqlite"); char *bobTesterFile = bc_tester_file("tmpZIDBob_cacheMismatch.sqlite"); resetGlobalParams(); /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bobTesterFile); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(bobTesterFile, &bobDB); /* make a first exchange */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), 0, int, "%x"); /* after the first exchange we shall have both pvs values at 1 and both rs1 identical and rs2 null, retrieve them from cache and check it */ /* first get each ZIDs, note give NULL as RNG context may lead to segfault in case of error(caches were not created correctly)*/ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)aliceDB, "alice@sip.linphone.org", selfZIDalice, NULL, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)bobDB, "bob@sip.linphone.org", selfZIDbob, NULL, NULL), 0, int, "%x"); /* then get the matching zuid in cache */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", selfZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidAlice, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)bobDB, "bob@sip.linphone.org", "alice@sip.linphone.org", selfZIDalice, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidBob, NULL), 0, int, "%x"); /* retrieve the values */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); /* and compare to expected */ /* rs1 is set and they are both the same */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); /* rs2 is unset(NULL) */ BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); /* pvs is equal to 1 */ BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* Modify Alice cache rs1 first byte value, it will cause a cache mismatch at next exchange */ colValuesAlice[0][0] += 1; BC_ASSERT_EQUAL(bzrtp_cache_write_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 1, NULL), 0, int, "%x"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); colValuesAlice[i]=NULL; free(colValuesBob[i]); colValuesBob[i]=NULL; } /* make a third exchange : we have a cache mismatch(on Bob side only), wich means rs1 will not be backed up in rs2 which shall be NULL again */ /* make a second exchange : we have a cache mismatch(both on Bob and Alice side), wich means rs1 will not be backed up in rs2 which shall be NULL again */ /* rs1 will be in sync has the SAS comparison will succeed and pvs will be set to 1*/ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), RET_CACHE_MISMATCH<<16|RET_CACHE_MISMATCH, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* Delete Alice cache rs1 first byte value, it will cause a cache mismatch at next exchange but only on Bob's side as Alice will not expect any valid cache */ free(colValuesAlice[0]); colValuesAlice[0] = NULL; colLengthAlice[0] = 0; colValuesAlice[2][0] = 0; /* reset pvs to 0 */ BC_ASSERT_EQUAL(bzrtp_cache_write_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); colValuesAlice[i]=NULL; free(colValuesBob[i]); colValuesBob[i]=NULL; } /* make a third exchange : we have a cache mismatch(on Bob side only), wich means rs1 will not be backed up in rs2 which shall be NULL again */ /* rs1 will be in sync has the SAS comparison will succeed and pvs will be set to 1*/ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), RET_CACHE_MISMATCH<<16, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); free(colValuesBob[i]); } sqlite3_close(aliceDB); sqlite3_close(bobDB); /* clean temporary files */ remove(aliceTesterFile); remove(bobTesterFile); bc_free(aliceTesterFile); bc_free(bobTesterFile); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } static void test_cache_sas_not_confirmed(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bobDB=NULL; uint8_t selfZIDalice[12]; uint8_t selfZIDbob[12]; int zuidAlice=0,zuidBob=0; const char *colNames[] = {"rs1", "rs2", "pvs"}; uint8_t *colValuesAlice[3]; size_t colLengthAlice[3]; uint8_t *colValuesBob[3]; size_t colLengthBob[3]; int i; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_cacheSASNotConfirmed.sqlite"); char *bobTesterFile = bc_tester_file("tmpZIDBob_cacheSasNotConfirmed.sqlite"); resetGlobalParams(); /* init columns values pointers */ for (i=0; i<3; i++) { colValuesAlice[i] = NULL; colValuesBob[i] = NULL; } /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bobTesterFile); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(bobTesterFile, &bobDB); /* make a first exchange, Alice is instructed to not validate the SAS */ BC_ASSERT_EQUAL(multichannel_exchange_pvs_params(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org", TRUE, 0, 0), 0, int, "%x"); /* after the first exchange we shall have alice pvs at 0 and bob at 1 and both rs1 identical and rs2 null, retrieve them from cache and check it */ /* first get each ZIDs, note give NULL as RNG context may lead to segfault in case of error(caches were not created correctly)*/ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)aliceDB, "alice@sip.linphone.org", selfZIDalice, NULL, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)bobDB, "bob@sip.linphone.org", selfZIDbob, NULL, NULL), 0, int, "%x"); /* then get the matching zuid in cache */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", selfZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidAlice, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)bobDB, "bob@sip.linphone.org", "alice@sip.linphone.org", selfZIDalice, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidBob, NULL), 0, int, "%x"); /* retrieve the values */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); /* and compare to expected */ /* rs1 is set and they are both the same */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); /* rs2 is unset(NULL) */ BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); /* pvs is equal to 0 for Alice(actually NULL, so length is 0 and has no value which is considered 0 by the getPeerSecrets function) and 1 for Bob */ BC_ASSERT_EQUAL(colLengthAlice[2], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); colValuesAlice[i] = NULL; } /* make a second exchange, the PVS flag returned by both side shall be 0 as Alice did not validate hers on previous exchange */ /* but let them both validate this one */ BC_ASSERT_EQUAL(multichannel_exchange_pvs_params(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org", TRUE, 0, 0), 0, int, "%x"); /* read new values in cache, ZIDs and zuids must be identical, read alice first to be able to check rs2 with old rs1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); /* check what is now rs2 is the old rs1 */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[0], 32), 0, int, "%d"); /* colValuesBob, still old values from before the second exchange */ /* free buffers */ for (i=0; i<3; i++) { free(colValuesBob[i]); colValuesBob[i] = NULL; } /* so read bob updated values and compare rs1, rs2 and check pvs is at 1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[1], 32), 0, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); colValuesAlice[i] = NULL; } /* make a third exchange, the PVS flag returned by both side shall be 1 */ BC_ASSERT_EQUAL(multichannel_exchange_pvs_params(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org", TRUE, 1, 1), 0, int, "%x"); /* read new values in cache, ZIDs and zuids must be identical, read alice first to be able to check rs2 with old rs1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); /* check what is now rs2 is the old rs1 */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[0], 32), 0, int, "%d"); /* colValuesBob, still old values from before the second exchange */ /* free buffers */ for (i=0; i<3; i++) { free(colValuesBob[i]); colValuesBob[i] = NULL; } /* so read bob updated values and compare rs1, rs2 and check pvs is at 1 */ /* so read bob updated values and compare rs1, rs2 and check pvs is still at 1 */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[1], colValuesBob[1], 32), 0, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); free(colValuesBob[i]); } sqlite3_close(aliceDB); sqlite3_close(bobDB); /* clean temporary files */ remove(aliceTesterFile); remove(bobTesterFile); bc_free(aliceTesterFile); bc_free(bobTesterFile); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } static int test_auxiliary_secret_params(uint8_t *aliceAuxSecret, size_t aliceAuxSecretLength, uint8_t *bobAuxSecret, size_t bobAuxSecretLength, uint8_t aliceExpectedAuxSecretMismatch, uint8_t bobExpectedAuxSecretMismatch, uint8_t badTimingFlag) { int retval; clientContext_t Alice,Bob; uint64_t initialTime=0; uint64_t lastPacketSentTime=0; uint32_t aliceSSRC = ALICE_SSRC_BASE; uint32_t bobSSRC = BOB_SSRC_BASE; uint8_t setAuxSecretFlag=0; // switch to 1 once we've set the aux secret /*** Create the main channel */ if ((retval=setUpClientContext(&Alice, ALICE, aliceSSRC, NULL, NULL, NULL, NULL, NULL))!=0) { bzrtp_message("ERROR: can't init setup client context id %d\n", ALICE); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } if ((retval=setUpClientContext(&Bob, BOB, bobSSRC, NULL, NULL, NULL, NULL, NULL))!=0) { bzrtp_message("ERROR: can't init setup client context id %d\n", BOB); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } /*** Setup a transient auxiliary secret ***/ if (badTimingFlag==0) { setAuxSecretFlag=1; if (aliceAuxSecret != NULL) { if ((retval = bzrtp_setAuxiliarySharedSecret(Alice.bzrtpContext, aliceAuxSecret, aliceAuxSecretLength))!=0) { bzrtp_message("ERROR: can't set Auxiliary shared secret. id is %d\n", ALICE); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } } if (bobAuxSecret != NULL) { if ((retval = bzrtp_setAuxiliarySharedSecret(Bob.bzrtpContext, bobAuxSecret, bobAuxSecretLength))!=0) { bzrtp_message("ERROR: can't set Auxiliary shared secret. id is %d\n", BOB); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } } } /* start the ZRTP engine(it will send a hello packet )*/ if ((retval = bzrtp_startChannelEngine(Alice.bzrtpContext, aliceSSRC))!=0) { bzrtp_message("ERROR: bzrtp_startChannelEngine returned %0x, client id is %d SSRC is %d\n", retval, ALICE, aliceSSRC); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } if ((retval = bzrtp_startChannelEngine(Bob.bzrtpContext, bobSSRC))!=0) { bzrtp_message("ERROR: bzrtp_startChannelEngine returned %0x, client id is %d SSRC is %d\n", retval, BOB, bobSSRC); BC_ASSERT_EQUAL(retval, 0, uint32_t, "0x%08x"); return -1; } initialTime = getSimulatedTime(); while ((bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC)!= BZRTP_CHANNEL_SECURE || bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC)!= BZRTP_CHANNEL_SECURE) && (getSimulatedTime()-initialTime 1250 ) { /*higher re-emission timeout is 1200ms */ retval = bzrtp_resetRetransmissionTimer(Alice.bzrtpContext, aliceSSRC); retval +=bzrtp_resetRetransmissionTimer(Bob.bzrtpContext, bobSSRC); lastPacketSentTime=getSimulatedTime(); } if (badTimingFlag!=0 && setAuxSecretFlag < 2) { /* after the HelloPacket exchange has occurs, insert the auxSecret if we have the badTiming flag on */ setAuxSecretFlag ++; if (setAuxSecretFlag == 2) { // first time we process a clock tick will be sending Hello Message, at the second one we will already have processed them and it will be too late if (aliceAuxSecret != NULL) { BC_ASSERT_NOT_EQUAL(bzrtp_setAuxiliarySharedSecret(Alice.bzrtpContext, aliceAuxSecret, aliceAuxSecretLength), 0, int, "%d"); // we expect this insert to be rejected } if (bobAuxSecret != NULL) { BC_ASSERT_NOT_EQUAL(bzrtp_setAuxiliarySharedSecret(Bob.bzrtpContext, bobAuxSecret, bobAuxSecretLength), 0, int, "%d"); // we expect this insert to be rejected } } } } if ((retval=bzrtp_getChannelStatus(Alice.bzrtpContext, aliceSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Alice on channel1 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return -1; } if ((retval=bzrtp_getChannelStatus(Bob.bzrtpContext, bobSSRC))!=BZRTP_CHANNEL_SECURE) { bzrtp_message("Fail Bob on channel1 loss rate is %d", loosePacketPercentage); BC_ASSERT_EQUAL(retval, BZRTP_CHANNEL_SECURE, int, "%0x"); return -1; } bzrtp_message("ZRTP algo used during negotiation: Cipher: %s - KeyAgreement: %s - Hash: %s - AuthTag: %s - Sas Rendering: %s\n", bzrtp_cipher_toString(Alice.secrets->cipherAlgo), bzrtp_keyAgreement_toString(Alice.secrets->keyAgreementAlgo), bzrtp_hash_toString(Alice.secrets->hashAlgo), bzrtp_authtag_toString(Alice.secrets->authTagAlgo), bzrtp_sas_toString(Alice.secrets->sasAlgo)); if ((retval=compareSecrets(Alice.secrets, Bob.secrets, TRUE))!=0) { BC_ASSERT_EQUAL(retval, 0, int, "%d"); return -1; } // check aux secrets mismatch flag, they must be in sync if (Alice.secrets->auxSecretMismatch != Bob.secrets->auxSecretMismatch) { // if one is unset(AuxSecret is null so flag is at unset) then other can be unset(caught by previous if) or mismatch(this one) if (!( (Alice.secrets->auxSecretMismatch == BZRTP_AUXSECRET_UNSET && aliceAuxSecret == NULL && Bob.secrets->auxSecretMismatch == BZRTP_AUXSECRET_MISMATCH) || (Bob.secrets->auxSecretMismatch == BZRTP_AUXSECRET_UNSET && bobAuxSecret == NULL && Alice.secrets->auxSecretMismatch == BZRTP_AUXSECRET_MISMATCH))) { BC_FAIL("computed auxSecretMismatch flags differ from Alice to Bob"); return -1; } } // Do we have the expected mismatch on aux secret BC_ASSERT_EQUAL(Alice.secrets->auxSecretMismatch, aliceExpectedAuxSecretMismatch, uint8_t, "%d"); BC_ASSERT_EQUAL(Bob.secrets->auxSecretMismatch, bobExpectedAuxSecretMismatch, uint8_t, "%d"); /*** Destroy Contexts ***/ while (bzrtp_destroyBzrtpContext(Alice.bzrtpContext, aliceSSRC)>0 && aliceSSRC>=ALICE_SSRC_BASE) { aliceSSRC--; } while (bzrtp_destroyBzrtpContext(Bob.bzrtpContext, bobSSRC)>0 && bobSSRC>=BOB_SSRC_BASE) { bobSSRC--; } return 0; } static void test_auxiliary_secret() { uint8_t secret1[] = {0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9a, 0x00, 0xff}; uint8_t secret2[] = {0xfe, 0xed, 0xdc, 0xcb, 0xba, 0xa9, 0x98, 0x87, 0x76, 0x65, 0x54, 0x43}; resetGlobalParams(); // matching cases (expect mismatch flag to be 0) BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret1, sizeof(secret1), secret1, sizeof(secret1), BZRTP_AUXSECRET_MATCH, BZRTP_AUXSECRET_MATCH, 0), 0, int, "%d"); BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret2, sizeof(secret2), secret2, sizeof(secret2), BZRTP_AUXSECRET_MATCH, BZRTP_AUXSECRET_MATCH, 0), 0, int, "%d"); // mismatching cases (expect mismatch flag to be 1) // different secrets BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret1, sizeof(secret1), secret2, sizeof(secret2), BZRTP_AUXSECRET_MISMATCH, BZRTP_AUXSECRET_MISMATCH, 0), 0, int, "%d"); // only one side has a secret BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret1, sizeof(secret1), NULL, 0, BZRTP_AUXSECRET_MISMATCH, BZRTP_AUXSECRET_UNSET, 0), 0, int, "%d"); // no one has a secret BC_ASSERT_EQUAL(test_auxiliary_secret_params(NULL, 0, NULL, 0, BZRTP_AUXSECRET_UNSET, BZRTP_AUXSECRET_UNSET, 0), 0, int, "%d"); // same secret but one is one byte shorter BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret1, sizeof(secret1)-1, secret1, sizeof(secret1), BZRTP_AUXSECRET_MISMATCH, BZRTP_AUXSECRET_MISMATCH, 0), 0, int, "%d"); // matching secret, but inserted to late(last param is a flag to do that) so we expect unset BC_ASSERT_EQUAL(test_auxiliary_secret_params(secret1, sizeof(secret1), secret1, sizeof(secret1), BZRTP_AUXSECRET_UNSET, BZRTP_AUXSECRET_UNSET, 1), 0, int, "%d"); }; /** * scenario: * - create new users with empty zid cache * - start a firt exchange but abort it before conclusion * - make a successive exchange going correctly to the end */ static void test_abort_retry(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bobDB=NULL; uint8_t selfZIDalice[12]; uint8_t selfZIDbob[12]; int zuidAlice=0,zuidBob=0; const char *colNames[] = {"rs1", "rs2", "pvs"}; uint8_t *colValuesAlice[3]; size_t colLengthAlice[3]; uint8_t *colValuesBob[3]; size_t colLengthBob[3]; int i; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_abortRetry.sqlite"); char *bobTesterFile = bc_tester_file("tmpZIDBob_abortRetry.sqlite"); resetGlobalParams(); /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bobTesterFile); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(bobTesterFile, &bobDB); /* make a first exchange but abort it */ timeOutLimit = ABORT_NEGOTIATION_TIMEOUT; /* set timeout to ABORT_NEGOTIATION_TIMEOUT aborts an ongoing negotiation without errors */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), 0, int, "%x"); /* after the first exchange we shall have only self ZID, peer ZID must not be inserted in cache */ /* first get each ZIDs, note give NULL as RNG context may lead to segfault in case of error(caches were not created correctly)*/ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)aliceDB, "alice@sip.linphone.org", selfZIDalice, NULL, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock((void *)bobDB, "bob@sip.linphone.org", selfZIDbob, NULL, NULL), 0, int, "%x"); /* try to get the matching zuid in cache: it shall not be there as the negotiation didn't completed */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", selfZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidAlice, NULL), BZRTP_ERROR_CACHE_PEERNOTFOUND, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)bobDB, "bob@sip.linphone.org", "alice@sip.linphone.org", selfZIDalice, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidBob, NULL), BZRTP_ERROR_CACHE_PEERNOTFOUND, int, "%x"); /* make a second exchange */ resetGlobalParams(); /* this one goes to the end of it */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bobDB, "bob@sip.linphone.org"), 0, int, "%x"); /* after the exchange we shall have both pvs values at 1 and both rs1 identical and rs2 null, retrieve them from cache and check it */ /* first get each ZIDs, note give NULL as RNG context may lead to segfault in case of error(caches were not created correctly)*/ /* get the matching zuid in cache */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", selfZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidAlice, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)bobDB, "bob@sip.linphone.org", "alice@sip.linphone.org", selfZIDalice, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidBob, NULL), 0, int, "%x"); if (zuidAlice==0 || zuidBob==0) {//abort if we didn't retrieve valid zuid values, keep tmp sqlite files for inspection sqlite3_close(aliceDB); sqlite3_close(bobDB); return; } /* retrieve the values */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidAlice, "zrtp", colNames, colValuesAlice, colLengthAlice, 3, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)bobDB, zuidBob, "zrtp", colNames, colValuesBob, colLengthBob, 3, NULL), 0, int, "%x"); /* and compare to expected */ /* rs1 is set and they are both the same */ BC_ASSERT_EQUAL(colLengthAlice[0], 32, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[0], 32, int, "%d"); BC_ASSERT_EQUAL(memcmp(colValuesAlice[0], colValuesBob[0], 32), 0, int, "%d"); /* rs2 is unset(NULL) */ BC_ASSERT_EQUAL(colLengthAlice[1], 0, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[1], 0, int, "%d"); BC_ASSERT_PTR_NULL(colValuesAlice[1]); BC_ASSERT_PTR_NULL(colValuesBob[1]); /* pvs is equal to 1 */ BC_ASSERT_EQUAL(colLengthAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(colLengthBob[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesAlice[2], 1, int, "%d"); BC_ASSERT_EQUAL(*colValuesBob[2], 1, int, "%d"); /* free buffers */ for (i=0; i<3; i++) { free(colValuesBob[i]); colValuesBob[i]=NULL; } /* free buffers */ for (i=0; i<3; i++) { free(colValuesAlice[i]); free(colValuesBob[i]); } sqlite3_close(aliceDB); sqlite3_close(bobDB); /* clean temporary files */ remove(aliceTesterFile); remove(bobTesterFile); bc_free(aliceTesterFile); bc_free(bobTesterFile); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } static void test_active_flag(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bob1DB=NULL; sqlite3 *bob2DB=NULL; sqlite3 *bob3DB=NULL; sqlite3 *claire1DB=NULL; sqlite3 *claire2DB=NULL; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_activeFlag.sqlite"); char *bob1TesterFile = bc_tester_file("tmpZIDBob1_activeFlag.sqlite"); char *bob2TesterFile = bc_tester_file("tmpZIDBob2_activeFlag.sqlite"); char *bob3TesterFile = bc_tester_file("tmpZIDBob3_activeFlag.sqlite"); char *claire1TesterFile = bc_tester_file("tmpZIDClaire1_activeFlag.sqlite"); char *claire2TesterFile = bc_tester_file("tmpZIDClaire2_activeFlag.sqlite"); resetGlobalParams(); /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bob1TesterFile); remove(bob2TesterFile); remove(bob3TesterFile); remove(claire1TesterFile); remove(claire2TesterFile); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(bob1TesterFile, &bob1DB); bzrtptester_sqlite3_open(bob2TesterFile, &bob2DB); bzrtptester_sqlite3_open(bob3TesterFile, &bob3DB); bzrtptester_sqlite3_open(claire1TesterFile, &claire1DB); bzrtptester_sqlite3_open(claire2TesterFile, &claire2DB); /* make a first exchange alice <-> bob1, validate the SAS */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob1DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be valid(bob1 is active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_VALID, int, "%x"); /* make an exchange, alice <-> bob2, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob2DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be unknown(as it is the first exchange with bob2 which is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* make an exchange, alice <-> bob1, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob1DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be valid(bob1 is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_VALID, int, "%x"); /* make an exchange, alice <-> bob1, alice is instructed to reset the SAS */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionResetSAS(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob1DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be invalid(bob1 is still active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_INVALID, int, "%x"); /* make an exchange, alice <-> bob2, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob2DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be unknown(as it is the first exchange with bob2 which is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* make an exchange alice <-> bob2, validate the SAS */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob2DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be valid (bob2 is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_VALID, int, "%x"); /* make an exchange, alice <-> bob1, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob1DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be invalid(bob1 is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_INVALID, int, "%x"); /* make an exchange, alice <-> bob3, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob3DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be unknown(as it is the first exchange with bob3 which is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* introducing Claire */ /* ask alice what is the pvs status of the active claire uri: claire@sip.linphone.org, it shall still be unknown as alice never heard about clairee yet */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "claire@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* make an exchange, alice <-> claire1, alice is instructed to reset the SAS */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionResetSAS(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", claire1DB, "claire@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall still be unknown(bob3 is still active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* ask alice what is the pvs status of the active claire uri: claire@sip.linphone.org, it shall still be invalid */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "claire@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_INVALID, int, "%x"); /* make a first exchange alice <-> claire2, validate the SAS */ BC_ASSERT_EQUAL(multichannel_exchange(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", claire2DB, "claire@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall still be unknown(bob3 is still active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_UNKNOWN, int, "%x"); /* ask alice what is the pvs status of the active claire uri: claire@sip.linphone.org, it shall still be valid */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "claire@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_VALID, int, "%x"); /* make an exchange, alice <-> bob1, alice is instructed to not validate the SAS nor invalidate it */ BC_ASSERT_EQUAL(multichannel_exchange(defaultCryptoAlgoSelectionNoSASValidation(), defaultCryptoAlgoSelection(), defaultCryptoAlgoSelection(), aliceDB, "alice@sip.linphone.org", bob1DB, "bob@sip.linphone.org"), 0, int, "%x"); /* ask alice what is the pvs status of the active bob uri: bob@sip.linphone.org, it shall be invalid(bob1 is now active) */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "bob@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_INVALID, int, "%x"); /* ask alice what is the pvs status of the active claire uri: claire@sip.linphone.org, it shall still be valid */ BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(aliceDB, "claire@sip.linphone.org", NULL), BZRTP_CACHE_PEER_STATUS_VALID, int, "%x"); sqlite3_close(aliceDB); sqlite3_close(bob1DB); sqlite3_close(bob2DB); sqlite3_close(bob3DB); sqlite3_close(claire1DB); sqlite3_close(claire2DB); /* clean temporary files */ remove(aliceTesterFile); remove(bob1TesterFile); remove(bob2TesterFile); remove(bob3TesterFile); remove(claire1TesterFile); remove(claire2TesterFile); bc_free(aliceTesterFile); bc_free(bob1TesterFile); bc_free(bob2TesterFile); bc_free(bob3TesterFile); bc_free(claire1TesterFile); bc_free(claire2TesterFile); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } /* * Scenario: * - one thread runs exchanges * - one thread requests peerStatus */ #ifdef ZIDCACHE_ENABLED struct thread_argument { sqlite3 *db; bctbx_mutex_t *dbMutex; char *peerUri; int expectedStatus; uint64_t timeout; }; static void *test_cache_concurrent_access_getPeerStatus(void *arg) { struct thread_argument *param = arg; while (getSimulatedTime()timeout) { BC_ASSERT_EQUAL(bzrtp_cache_getPeerStatus_lock(param->db, param->peerUri, param->dbMutex), param->expectedStatus, int, "%x"); } return NULL; } #endif /* ZIDCACHE_ENABLED */ static void test_cache_concurrent_access(void) { #ifdef ZIDCACHE_ENABLED sqlite3 *aliceDB=NULL; sqlite3 *bobDB=NULL; sqlite3 *aliceDB2=NULL; sqlite3 *bobDB2=NULL; uint64_t timeout = 20000; // run a 20s simulation bctbx_mutex_t aliceMutex, bobMutex; struct thread_argument aliceParams,bobParams; bctbx_thread_t aliceThreadId, bobThreadId; char *aliceTesterFile = bc_tester_file("tmpZIDAlice_concurrentAccess.sqlite"); char *bobTesterFile = bc_tester_file("tmpZIDBob1_concurrentAccess.sqlite"); void *res; resetGlobalParams(); /* create tempory DB files, just try to clean them from dir before, just in case */ remove(aliceTesterFile); remove(bobTesterFile); /* open 2 connections on each file */ bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB); bzrtptester_sqlite3_open(aliceTesterFile, &aliceDB2); bzrtptester_sqlite3_open(bobTesterFile, &bobDB); bzrtptester_sqlite3_open(bobTesterFile, &bobDB2); /* init mutex */ bctbx_mutex_init(&aliceMutex, NULL); bctbx_mutex_init(&bobMutex, NULL); /* set alice parameter to start a thread checking for bob status */ aliceParams.db = aliceDB2; aliceParams.dbMutex = &aliceMutex; //aliceParams.dbMutex = NULL; aliceParams.peerUri = "bob@sip.linphone.org"; aliceParams.expectedStatus = BZRTP_CACHE_PEER_STATUS_VALID; aliceParams.timeout = timeout; /* set alice parameter to start a thread checking for bob status */ bobParams.db = bobDB2; bobParams.dbMutex = &bobMutex; //bobParams.dbMutex = NULL; bobParams.peerUri = "alice@sip.linphone.org"; bobParams.expectedStatus = BZRTP_CACHE_PEER_STATUS_VALID; bobParams.timeout = timeout; /* make a first exchange alice <-> bob, validate the SAS */ BC_ASSERT_EQUAL(multichannel_exchange_mutex(NULL, NULL, defaultCryptoAlgoSelection(), aliceDB, &aliceMutex, "alice@sip.linphone.org", bobDB, &bobMutex, "bob@sip.linphone.org"), 0, int, "%x"); /* launch get_peerStatus thread */ bctbx_thread_create(&bobThreadId, NULL, &test_cache_concurrent_access_getPeerStatus, &bobParams); bctbx_thread_create(&aliceThreadId, NULL, &test_cache_concurrent_access_getPeerStatus, &aliceParams); /* run more exchanges */ while (getSimulatedTime(). */ #include #include #include #include "bzrtp/bzrtp.h" #include "cryptoUtils.h" #include "testUtils.h" #include "bzrtpTest.h" /** * * Patterns generated using GNU ZRTP C++ */ #define KDF_TEST_NUMBER 9 uint8_t patternKDFLabel[KDF_TEST_NUMBER][28] = { "Responder SRTP master key", "Responder SRTP master salt", "SAS", "ZRTP Session Key", "retained secret", "Responder ZRTP key", "Initiator ZRTP key", "Responder HMAC key", "Initiator HMAC key"}; uint16_t patternKDFContextLength[KDF_TEST_NUMBER] = {56, 56, 56 , 56, 56, 56, 56, 56, 56}; uint16_t patternKDFHmacLength[KDF_TEST_NUMBER] = {16, 14, 32, 32, 32, 16, 16, 32, 32}; uint8_t patternKDFOutput[KDF_TEST_NUMBER][32] = { {0xf2, 0xcf, 0xc5, 0x72, 0x4a, 0xcf, 0xd7, 0xb5, 0x0e, 0x89, 0xbe, 0xd8, 0xa5, 0xc9, 0xf2, 0xcd}, {0x3c, 0x8f, 0xb1, 0x3f, 0xd6, 0xca, 0x83, 0xfb, 0xd6, 0xaf, 0x3b, 0xd5, 0xab, 0x1c}, {0xd2, 0x69, 0xb0, 0x71, 0x8a, 0xd5, 0x8f, 0x3c, 0x8e, 0x0d, 0xcf, 0x45, 0x35, 0x81, 0x1d, 0x95, 0x18, 0x47, 0x31, 0x41, 0x28, 0x31, 0x9f, 0x54, 0xc6, 0x06, 0x59, 0x5b, 0x52, 0x2c, 0x52, 0x87}, {0xc3, 0x63, 0x39, 0x0b, 0xc9, 0xee, 0x13, 0xaa, 0x9e, 0x6a, 0x00, 0x52, 0x84, 0x09, 0xdd, 0xc9, 0x07, 0x17, 0xe0, 0x1d, 0x1a, 0xe2, 0xdc, 0xf9, 0xa7, 0x0a, 0xb9, 0x88, 0x86, 0xac, 0x69, 0x75}, {0xa7, 0x27, 0x2d, 0xee, 0x77, 0xe7, 0x99, 0x0c, 0x24, 0xb2, 0x0b, 0x9f, 0x54, 0xba, 0x2b, 0x61, 0x7a, 0x23, 0xd8, 0xa5, 0x97, 0x65, 0x82, 0x9b, 0x78, 0x07, 0x2b, 0xc5, 0x12, 0x99, 0x01, 0x5c}, {0x75, 0x76, 0x7d, 0x41, 0x82, 0x9c, 0xb6, 0x97, 0xc7, 0xa2, 0x0d, 0x68, 0x12, 0xea, 0xdb, 0x9a}, {0x08, 0xa8, 0x21, 0x46, 0x0c, 0xbd, 0x6c, 0xe0, 0xf1, 0xfe, 0x3c, 0x4c, 0x70, 0x4e, 0x3b, 0xea}, {0x21, 0x69, 0xf1, 0x5c, 0x1b, 0x85, 0xa2, 0x39, 0x77, 0x02, 0x4d, 0x5e, 0xa7, 0x1f, 0x48, 0x16, 0x23, 0xfe, 0x94, 0xdc, 0x1a, 0xab, 0xf4, 0x89, 0xb8, 0x74, 0x4d, 0x0d, 0x37, 0x3d, 0x7c, 0xab}, {0x1d, 0x11, 0x7b, 0xef, 0xd6, 0x27, 0xcc, 0x5a, 0x80, 0xd0, 0x23, 0x6e, 0x8d, 0x49, 0x6b, 0xe4, 0x2a, 0x6a, 0x01, 0x18, 0xac, 0x41, 0x59, 0xe8, 0x61, 0xb9, 0xb1, 0x4a, 0x78, 0x4b, 0x5d, 0x3f} }; uint8_t patternKDFContext[KDF_TEST_NUMBER][56] = { {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f}, {0xf0, 0x1f, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x24, 0xad, 0xfb, 0x94, 0xa9, 0x06, 0x90, 0xcf, 0x9c, 0xed, 0x13, 0x99, 0x8c, 0x51, 0x34, 0x7d, 0xc4, 0x35, 0xa7, 0x4d, 0x75, 0x25, 0x44, 0x80, 0xc2, 0x11, 0xf2, 0xd3, 0x20, 0x5e, 0xa7, 0x1b, 0x4b, 0xa4, 0xee, 0xc7, 0x8e, 0xb7, 0x35, 0x75, 0x28, 0xe5, 0x6f, 0xcf, 0x4f, 0x74, 0x9f} }; static void test_zrtpKDF(void) { int i; uint8_t keyKDF[32] = {0x33, 0xe6, 0x6c, 0x01, 0xca, 0x6f, 0xe6, 0x4f, 0xb7, 0x6f, 0xfd, 0xe3, 0x1c, 0xab, 0xc0, 0xfb, 0xad, 0x3d, 0x31, 0x02, 0x67, 0x6b, 0x0c, 0x09, 0x0f, 0xc9, 0x96, 0x38, 0x1e, 0x0a, 0x8c, 0x2f}; uint8_t output[32]; for (i=0; isupportedHash, types, typesCount); helloMessage->hc = typesCount; break; case ZRTP_CIPHERBLOCK_TYPE: memcpy(helloMessage->supportedCipher, types, typesCount); helloMessage->cc = typesCount; break; case ZRTP_AUTHTAG_TYPE: memcpy(helloMessage->supportedAuthTag, types, typesCount); helloMessage->ac = typesCount; break; case ZRTP_KEYAGREEMENT_TYPE: memcpy(helloMessage->supportedKeyAgreement, types, typesCount); helloMessage->kc = typesCount; break; case ZRTP_SAS_TYPE: memcpy(helloMessage->supportedSas, types, typesCount); helloMessage->sc = typesCount; break; } } static int compareAllAlgoTypes(bzrtpChannelContext_t *zrtpChannelContext, uint8_t expectedHashAlgo, uint8_t expectedCipherAlgo, uint8_t expectedAuthTagAlgo, uint8_t expectedKeyAgreementAlgo, uint8_t expectedSasAlgo) { return zrtpChannelContext->hashAlgo == expectedHashAlgo && zrtpChannelContext->cipherAlgo == expectedCipherAlgo && zrtpChannelContext->authTagAlgo == expectedAuthTagAlgo && zrtpChannelContext->keyAgreementAlgo == expectedKeyAgreementAlgo && zrtpChannelContext->sasAlgo == expectedSasAlgo; } static int compareAllAlgoTypesWithExpectedChangedOnly(bzrtpChannelContext_t *zrtpChannelContext, uint8_t expectedAlgoType, uint8_t expectedType) { switch(expectedAlgoType) { case ZRTP_HASH_TYPE: return compareAllAlgoTypes(zrtpChannelContext, expectedType, ZRTP_CIPHER_AES1, ZRTP_AUTHTAG_HS32, getDefaultKeyAgreementAlgo(), ZRTP_SAS_B32); case ZRTP_CIPHERBLOCK_TYPE: return compareAllAlgoTypes(zrtpChannelContext, ZRTP_HASH_S256, expectedType, ZRTP_AUTHTAG_HS32, getDefaultKeyAgreementAlgo(), ZRTP_SAS_B32); case ZRTP_AUTHTAG_TYPE: return compareAllAlgoTypes(zrtpChannelContext, ZRTP_HASH_S256, ZRTP_CIPHER_AES1, expectedType, getDefaultKeyAgreementAlgo(), ZRTP_SAS_B32); case ZRTP_KEYAGREEMENT_TYPE: return compareAllAlgoTypes(zrtpChannelContext, ZRTP_HASH_S256, ZRTP_CIPHER_AES1, ZRTP_AUTHTAG_HS32, expectedType, ZRTP_SAS_B32); case ZRTP_SAS_TYPE: return compareAllAlgoTypes(zrtpChannelContext, ZRTP_HASH_S256, ZRTP_CIPHER_AES1, ZRTP_AUTHTAG_HS32, getDefaultKeyAgreementAlgo(), expectedType); default: return compareAllAlgoTypes(zrtpChannelContext, ZRTP_HASH_S256, ZRTP_CIPHER_AES1, ZRTP_AUTHTAG_HS32, getDefaultKeyAgreementAlgo(), ZRTP_SAS_B32); } } static int testAlgoType(uint8_t algoType, uint8_t *packetTypes, uint8_t packetTypesCount, uint8_t *contextTypes, uint8_t contextTypesCount, uint8_t expectedType) { int retval; bzrtpContext_t *zrtpContext = bzrtp_createBzrtpContext(); bzrtpPacket_t *helloPacket = NULL; bzrtp_initBzrtpContext(zrtpContext, 0x12345678); if (contextTypes != NULL) { bzrtp_setSupportedCryptoTypes(zrtpContext, algoType, contextTypes, contextTypesCount); } helloPacket = bzrtp_createZrtpPacket(zrtpContext, zrtpContext->channelContext[0], MSGTYPE_HELLO, &retval); if (packetTypes != NULL) { bzrtpHelloMessage_t *helloMessage = (bzrtpHelloMessage_t *)helloPacket->messageData; setHelloMessageAlgo(helloMessage, algoType, packetTypes, packetTypesCount); } BC_ASSERT_FALSE(bzrtp_cryptoAlgoAgreement(zrtpContext, zrtpContext->channelContext[0], helloPacket->messageData)); retval = compareAllAlgoTypesWithExpectedChangedOnly(zrtpContext->channelContext[0], algoType, expectedType); bzrtp_freeZrtpPacket(helloPacket); bzrtp_destroyBzrtpContext(zrtpContext, 0x12345678); return retval; } static int testAlgoTypeWithPacket(uint8_t algoType, uint8_t *types, uint8_t typesCount, uint8_t expectedType) { return testAlgoType(algoType, types, typesCount, NULL, 0, expectedType); } static int testAlgoTypeWithContext(uint8_t algoType, uint8_t *types, uint8_t typesCount, uint8_t expectedType) { return testAlgoType(algoType, NULL, 0, types, typesCount, expectedType); } struct st_algo_type_with_packet { uint8_t types[7]; uint8_t typesCount; uint8_t expectedType; }; struct st_algo_type_with_context { uint8_t types[7]; uint8_t typesCount; uint8_t expectedType; }; struct st_algo_type { uint8_t packetTypes[7]; uint8_t packetTypesCount; uint8_t contextTypes[7]; uint8_t contextTypesCount; uint8_t expectedType; }; static void test_algoAgreement(void) { struct st_algo_type_with_packet agreement_types_with_packet[] = { { {ZRTP_KEYAGREEMENT_DH2k}, 1, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_DH3k}, 1, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k}, 2, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_EC52}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_DH3k}, 3, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 4, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52}, 4, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type_with_packet *agreement_type_with_packet; struct st_algo_type_with_context agreement_types_with_context[] = { { {ZRTP_KEYAGREEMENT_DH2k}, 1, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_DH3k}, 1, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_EC25}, 1, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k}, 2, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_EC52}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_EC25}, 2, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_DH3k}, 3, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 4, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52}, 4, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type_with_context *agreement_type_with_context; struct st_algo_type agreement_types[] = { { {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k}, 2, {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 4, {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 2, {ZRTP_KEYAGREEMENT_EC52, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_DH2k}, 4, ZRTP_KEYAGREEMENT_DH3k }, { {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52}, 4, {ZRTP_KEYAGREEMENT_DH2k, ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_EC25, ZRTP_KEYAGREEMENT_EC52}, 4, ZRTP_KEYAGREEMENT_DH2k }, { {ZRTP_UNSET_ALGO}, 0, {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type *agreement_type; struct st_algo_type_with_packet cipher_types_with_packet[] = { { {ZRTP_CIPHER_AES1}, 1, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES3}, 1, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES3}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 3, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES3}, 6, ZRTP_CIPHER_AES1 }, { {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type_with_packet *cipher_type_with_packet; struct st_algo_type_with_context cipher_types_with_context[] = { { {ZRTP_CIPHER_AES1}, 1, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES3}, 1, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS1}, 1, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES3}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 3, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1}, 3, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES3}, 6, ZRTP_CIPHER_AES1 }, { {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type_with_context *cipher_type_with_context; struct st_algo_type cipher_types[] = { { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, {ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES3}, 2, ZRTP_CIPHER_AES1 }, { {ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES3}, 2, {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES1}, 2, {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, ZRTP_CIPHER_AES3 }, { {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES3, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES1}, 6, {ZRTP_CIPHER_2FS3, ZRTP_CIPHER_2FS2, ZRTP_CIPHER_2FS1, ZRTP_CIPHER_AES1, ZRTP_CIPHER_AES2, ZRTP_CIPHER_AES3}, 6, ZRTP_CIPHER_AES1 }, { {ZRTP_UNSET_ALGO}, 0, {ZRTP_UNSET_ALGO}, 0, 0 } }; struct st_algo_type *cipher_type; /* check defaults */ BC_ASSERT_TRUE(testAlgoTypeWithPacket(ZRTP_UNSET_ALGO, NULL, 0, getDefaultKeyAgreementAlgo())); /* key agreement type */ agreement_type_with_packet = &agreement_types_with_packet[0]; while (agreement_type_with_packet->typesCount > 0) { BC_ASSERT_TRUE(testAlgoTypeWithPacket(ZRTP_KEYAGREEMENT_TYPE, agreement_type_with_packet->types, agreement_type_with_packet->typesCount, agreement_type_with_packet->expectedType)); agreement_type_with_packet++; } agreement_type_with_context = &agreement_types_with_context[0]; while (agreement_type_with_context->typesCount > 0) { BC_ASSERT_TRUE(testAlgoTypeWithContext(ZRTP_KEYAGREEMENT_TYPE, agreement_type_with_context->types, agreement_type_with_context->typesCount, agreement_type_with_context->expectedType)); agreement_type_with_context++; } agreement_type = &agreement_types[0]; while (agreement_type->packetTypesCount > 0) { BC_ASSERT_TRUE(testAlgoType(ZRTP_KEYAGREEMENT_TYPE, agreement_type->packetTypes, agreement_type->packetTypesCount, agreement_type->contextTypes, agreement_type->contextTypesCount, agreement_type->expectedType)); agreement_type++; } /* cipher type */ cipher_type_with_packet = &cipher_types_with_packet[0]; while (cipher_type_with_packet->typesCount > 0) { BC_ASSERT_TRUE(testAlgoTypeWithPacket(ZRTP_CIPHERBLOCK_TYPE, cipher_type_with_packet->types, cipher_type_with_packet->typesCount, cipher_type_with_packet->expectedType)); cipher_type_with_packet++; } cipher_type_with_context = &cipher_types_with_context[0]; while (cipher_type_with_context->typesCount > 0) { BC_ASSERT_TRUE(testAlgoTypeWithContext(ZRTP_CIPHERBLOCK_TYPE, cipher_type_with_context->types, cipher_type_with_context->typesCount, cipher_type_with_context->expectedType)); cipher_type_with_context++; } cipher_type = &cipher_types[0]; while (cipher_type->packetTypesCount > 0) { BC_ASSERT_TRUE(testAlgoType(ZRTP_CIPHERBLOCK_TYPE, cipher_type->packetTypes, cipher_type->packetTypesCount, cipher_type->contextTypes, cipher_type->contextTypesCount, cipher_type->expectedType)); cipher_type++; } } static int compareAlgoTypes(uint8_t actualTypes[7], uint8_t actualTypesCount, uint8_t expectedTypes[7], uint8_t expectedTypesCount) { int i; if (actualTypesCount != expectedTypesCount) { return 0; } for (i=0; icontextTypesCount > 0) { BC_ASSERT_TRUE(testAlgoSetterGetter(ZRTP_KEYAGREEMENT_TYPE, agreement_type->contextTypes, agreement_type->contextTypesCount, agreement_type->expectedTypes, agreement_type->expectedTypesCount)); agreement_type++; } /* ECDH agreement type */ if (bctbx_key_agreement_algo_list()&BCTBX_ECDH_X25519) { agreement_type = &ecdh_agreement_types[0]; while (agreement_type->contextTypesCount > 0) { BC_ASSERT_TRUE(testAlgoSetterGetter(ZRTP_KEYAGREEMENT_TYPE, agreement_type->contextTypes, agreement_type->contextTypesCount, agreement_type->expectedTypes, agreement_type->expectedTypesCount)); agreement_type++; } } /* cipher type */ cipher_type = &cipher_types[0]; while (cipher_type->contextTypesCount > 0) { BC_ASSERT_TRUE(testAlgoSetterGetter(ZRTP_CIPHERBLOCK_TYPE, cipher_type->contextTypes, cipher_type->contextTypesCount, cipher_type->expectedTypes, cipher_type->expectedTypesCount)); cipher_type++; } } #define ZRTP_AUTHTAG_FAKE_1 0x41 #define ZRTP_AUTHTAG_FAKE_2 0x42 #define ZRTP_AUTHTAG_FAKE_3 0x43 #define ZRTP_AUTHTAG_FAKE_4 0x44 #define ZRTP_AUTHTAG_FAKE_5 0x45 static int testAddMandatoryCryptoTypesIfNeeded(uint8_t algoType, uint8_t *algoTypes, uint8_t algoTypesCount, uint8_t *expectedTypes, uint8_t expectedTypesCount) { bzrtp_addMandatoryCryptoTypesIfNeeded(algoType, algoTypes, &algoTypesCount); return compareAlgoTypes(algoTypes, algoTypesCount, expectedTypes, expectedTypesCount); } struct st_add_crypto { uint8_t algoType; uint8_t algoTypes[7]; uint8_t algoTypesCount; uint8_t expectedTypes[7]; uint8_t expectedTypesCount; }; static void test_addMandatoryCryptoTypesIfNeeded(void) { struct st_add_crypto crypto_types[] = { /* mandatory types */ { ZRTP_HASH_TYPE, {ZRTP_UNSET_ALGO}, 0, {ZRTP_HASH_S256}, 1 }, { ZRTP_CIPHERBLOCK_TYPE, {ZRTP_UNSET_ALGO}, 0, {ZRTP_CIPHER_AES1}, 1 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_UNSET_ALGO}, 0, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 2 }, { ZRTP_KEYAGREEMENT_TYPE, {ZRTP_UNSET_ALGO}, 0, {ZRTP_KEYAGREEMENT_DH3k}, 1 }, { ZRTP_KEYAGREEMENT_TYPE, {ZRTP_KEYAGREEMENT_X255}, 1, {ZRTP_KEYAGREEMENT_DH3k, ZRTP_KEYAGREEMENT_X255}, 2 }, { ZRTP_SAS_TYPE, {ZRTP_UNSET_ALGO}, 0, {ZRTP_SAS_B32}, 1 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 2, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 2 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32}, 2, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32}, 2 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK64}, 2, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_HS32}, 3 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64}, 2, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 4 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3}, 5, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS80}, 6, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32}, 7 }, /* overrride */ { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4, ZRTP_AUTHTAG_FAKE_5}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_FAKE_4}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32}, 7 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_FAKE_4, ZRTP_AUTHTAG_HS80}, 7, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_FAKE_1, ZRTP_AUTHTAG_FAKE_2, ZRTP_AUTHTAG_FAKE_3, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32}, 7 }, /* uniqueness */ { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64}, 4, {ZRTP_AUTHTAG_SK32, ZRTP_AUTHTAG_SK64, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 4 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS32}, 4, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 2 }, { ZRTP_AUTHTAG_TYPE, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80, ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 4, {ZRTP_AUTHTAG_HS32, ZRTP_AUTHTAG_HS80}, 2 }, { 0, {ZRTP_UNSET_ALGO}, 0, {ZRTP_UNSET_ALGO}, 0, } }; struct st_add_crypto *crypto_type; crypto_type = &crypto_types[0]; while (crypto_type->algoTypesCount > 0) { BC_ASSERT_TRUE(testAddMandatoryCryptoTypesIfNeeded(crypto_type->algoType, crypto_type->algoTypes, crypto_type->algoTypesCount, crypto_type->expectedTypes, crypto_type->expectedTypesCount)); crypto_type++; } } static test_t crypto_utils_tests[] = { TEST_NO_TAG("zrtpKDF", test_zrtpKDF), TEST_NO_TAG("CRC32", test_CRC32), TEST_NO_TAG("algo agreement", test_algoAgreement), TEST_NO_TAG("context algo setter and getter", test_algoSetterGetter), TEST_NO_TAG("adding mandatory crypto algorithms if needed", test_addMandatoryCryptoTypesIfNeeded) }; test_suite_t crypto_utils_test_suite = { "Crypto Utils", NULL, NULL, NULL, NULL, sizeof(crypto_utils_tests) / sizeof(crypto_utils_tests[0]), crypto_utils_tests }; bzrtp-4.4.13/test/bzrtpParserTest.c000066400000000000000000003477031364144501400172650ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "bzrtp/bzrtp.h" #include "typedef.h" #include "packetParser.h" #include "cryptoUtils.h" #include "zidCache.h" #include "testUtils.h" #include "bzrtpTest.h" #ifndef _WIN32 #include #else #include #endif /** * Test pattern : the packet data in the patternZRTPPackets, length, sequence number and SSRC in patternZRTPMetaData * Pattern generated from wireshark trace * */ #define TEST_PACKET_NUMBER 9 /* meta data: length, sequence number, SSRC */ static uint32_t patternZRTPMetaData[TEST_PACKET_NUMBER][3] = { {136, 0x09f1, 0x12345678}, /* hello */ {136, 0x02ce, 0x87654321}, /* hello */ {28, 0x09f2, 0x12345678}, /* hello ack */ {132, 0x02d0, 0x87654321}, /* commit */ {484, 0x09f5, 0x12345678}, /* dhpart1 */ {484, 0x02d1, 0x87654321}, /* dhpart2 */ {92, 0x09f6, 0x12345678}, /* confirm 1 */ {92, 0x02d2, 0x87654321}, /* confirm 2 */ {28, 0x09f7, 0x12345678} /* conf2ACK*/ }; static const uint8_t patternZRTPHelloHash12345678[70]="1.10 13e9f407367895861f0eee6707ba30aca05a0ad9997625e9279ad5d08485aa9d"; static const uint8_t patternZRTPHelloHash87654321[70]="1.10 8a286f762a00f21907fe937801894ce4f4ac6a7d2b9acd61eb25b014f905df77"; static const uint8_t patternZRTPPackets[TEST_PACKET_NUMBER][512] = { /* This is a Hello packet, sequence number is 0x09f1, SSRC 0x12345678 */ {0x10, 0x00, 0x09, 0xf1, 0x5a, 0x52, 0x54, 0x50, 0x12, 0x34, 0x56, 0x78, 0x50, 0x5a, 0x00, 0x1e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x20, 0x20, 0x31, 0x2e, 0x31, 0x30, 0x4c, 0x49, 0x4e, 0x50, 0x48, 0x4f, 0x4e, 0x45, 0x2d, 0x5a, 0x52, 0x54, 0x50, 0x43, 0x50, 0x50, 0xe8, 0xd5, 0x26, 0xc1, 0x3a, 0x0c, 0x4c, 0x6a, 0xce, 0x18, 0xaa, 0xc7, 0xc4, 0xa4, 0x07, 0x0e, 0x65, 0x7a, 0x4d, 0xca, 0x78, 0xf2, 0xcc, 0xcd, 0x20, 0x50, 0x38, 0x73, 0xe9, 0x7e, 0x08, 0x29, 0x7e, 0xb0, 0x04, 0x97, 0xc0, 0xfe, 0xb2, 0xc9, 0x24, 0x31, 0x49, 0x7f, 0x00, 0x01, 0x12, 0x31, 0x53, 0x32, 0x35, 0x36, 0x41, 0x45, 0x53, 0x31, 0x48, 0x53, 0x33, 0x32, 0x48, 0x53, 0x38, 0x30, 0x44, 0x48, 0x33, 0x6b, 0x44, 0x48, 0x32, 0x6b, 0x4d, 0x75, 0x6c, 0x74, 0x42, 0x33, 0x32, 0x20, 0xa0, 0xfd, 0x0f, 0xad, 0xeb, 0xe0, 0x86, 0x56, 0xe3, 0x65, 0x81, 0x02}, /* This is a Hello packet, sequence number is 0x02ce, SSRC 0x87654321 */ {0x10, 0x00, 0x02, 0xce, 0x5a, 0x52, 0x54, 0x50, 0x87, 0x65, 0x43, 0x21, 0x50, 0x5a, 0x00, 0x1e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x20, 0x20, 0x31, 0x2e, 0x31, 0x30, 0x4c, 0x49, 0x4e, 0x50, 0x48, 0x4f, 0x4e, 0x45, 0x2d, 0x5a, 0x52, 0x54, 0x50, 0x43, 0x50, 0x50, 0x8d, 0x0f, 0x5a, 0x20, 0x79, 0x97, 0x42, 0x01, 0x99, 0x45, 0x45, 0xf7, 0x0e, 0x31, 0x06, 0xe1, 0x05, 0xc0, 0xb9, 0x24, 0xe9, 0xc9, 0x78, 0xc7, 0x38, 0xf5, 0x97, 0x48, 0xef, 0x42, 0x6a, 0x3e, 0x92, 0x42, 0xc2, 0xcf, 0x44, 0xee, 0x9c, 0x65, 0xca, 0x58, 0x78, 0xf1, 0x00, 0x01, 0x12, 0x31, 0x53, 0x32, 0x35, 0x36, 0x41, 0x45, 0x53, 0x31, 0x48, 0x53, 0x33, 0x32, 0x48, 0x53, 0x38, 0x30, 0x44, 0x48, 0x33, 0x6b, 0x44, 0x48, 0x32, 0x6b, 0x4d, 0x75, 0x6c, 0x74, 0x42, 0x33, 0x32, 0x20, 0xb3, 0x90, 0x91, 0x95, 0xe4, 0x67, 0xa3, 0x21, 0xe3, 0x5f, 0x9c, 0x92}, /* This is a Hello ack packet, sequence number is 0x09f2, SSRC 0x12345678*/ {0x10, 0x00, 0x09, 0xf2, 0x5a, 0x52, 0x54, 0x50, 0x12, 0x34, 0x56, 0x78, 0x50, 0x5a, 0x00, 0x03, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x41, 0x43, 0x4b, 0x77, 0x0e, 0x44, 0x07}, /* This is a Commit packet, sequence number is 0x02d0, SSRC 0x87654321 */ {0x10, 0x00, 0x02, 0xd0, 0x5a, 0x52, 0x54, 0x50, 0x87, 0x65, 0x43, 0x21, 0x50, 0x5a, 0x00, 0x1d, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x20, 0xd9, 0xff, 0x14, 0x8b, 0x34, 0xaa, 0x69, 0xe9, 0x33, 0xc1, 0x62, 0xe6, 0x6b, 0xe8, 0xcd, 0x9d, 0xe3, 0x0f, 0xb7, 0x6a, 0xe8, 0x6a, 0x62, 0x2b, 0xcb, 0xe4, 0x6b, 0x91, 0x05, 0xc7, 0xc8, 0x7e, 0x92, 0x42, 0xc2, 0xcf, 0x44, 0xee, 0x9c, 0x65, 0xca, 0x58, 0x78, 0xf1, 0x53, 0x32, 0x35, 0x36, 0x41, 0x45, 0x53, 0x31, 0x48, 0x53, 0x33, 0x32, 0x44, 0x48, 0x33, 0x6b, 0x42, 0x33, 0x32, 0x20, 0x1e, 0xc0, 0xfe, 0x2e, 0x72, 0x06, 0x4d, 0xfb, 0xfc, 0x92, 0x02, 0x8c, 0x03, 0x0f, 0xb8, 0xf8, 0x91, 0xb4, 0xe7, 0x96, 0xac, 0x25, 0xfd, 0xf9, 0x68, 0xc6, 0xe9, 0x67, 0xa9, 0x42, 0xb1, 0x5b, 0xbb, 0x6d, 0x9c, 0xd2, 0x4b, 0x13, 0xa9, 0xae, 0x25, 0x5c, 0xa9, 0xc1}, /* This is a DHPart1 packet, sequence number is 0x09f5, SSRC 0x12345678 */ {0x10, 0x00, 0x09, 0xf5, 0x5a, 0x52, 0x54, 0x50, 0x12, 0x34, 0x56, 0x78, 0x50, 0x5a, 0x00, 0x75, 0x44, 0x48, 0x50, 0x61, 0x72, 0x74, 0x31, 0x20, 0x28, 0x7c, 0x28, 0xe4, 0xd7, 0x3d, 0x14, 0x39, 0xb5, 0x6d, 0x1c, 0x47, 0x9d, 0x59, 0x0a, 0xf2, 0x10, 0x33, 0xde, 0x6b, 0xd5, 0x2b, 0xfb, 0x26, 0xa5, 0x87, 0x4d, 0xe9, 0x20, 0x6b, 0x9f, 0xdd, 0xab, 0xbc, 0xc6, 0x8d, 0xbd, 0x5d, 0xe6, 0x67, 0x00, 0x69, 0x44, 0xb1, 0x84, 0x2c, 0x27, 0x10, 0x8c, 0x4e, 0x58, 0x8a, 0xed, 0x7e, 0x8b, 0x44, 0x2c, 0x3a, 0x13, 0x02, 0xdf, 0x58, 0xb6, 0xda, 0x80, 0x55, 0xec, 0xb0, 0x20, 0xc7, 0x76, 0x50, 0xc4, 0x1b, 0xa8, 0x26, 0x11, 0x5c, 0xf5, 0x71, 0x7e, 0xb4, 0x86, 0x22, 0x17, 0xde, 0x14, 0x08, 0x46, 0x5c, 0xac, 0x88, 0x8c, 0x41, 0x6b, 0x95, 0x22, 0xba, 0xf8, 0x3e, 0x67, 0x20, 0x94, 0xa0, 0x84, 0xa3, 0x93, 0x41, 0x9a, 0x1a, 0x7c, 0x2f, 0x04, 0xf4, 0x14, 0x3f, 0x11, 0x54, 0x02, 0xba, 0xee, 0xc2, 0x20, 0xfa, 0x38, 0xf7, 0xba, 0xa4, 0xbf, 0x4a, 0x70, 0x02, 0x68, 0xdc, 0xb2, 0xe9, 0x1a, 0x8a, 0x87, 0xa5, 0xe4, 0x9c, 0x42, 0x07, 0x82, 0x26, 0xb4, 0xda, 0xe3, 0x1b, 0xdc, 0x78, 0xc7, 0xd8, 0xa8, 0x00, 0x5c, 0x00, 0x14, 0xe4, 0x00, 0xfe, 0x6c, 0x2d, 0xce, 0x62, 0xc9, 0x71, 0x5d, 0xed, 0x4e, 0x66, 0x9f, 0xf5, 0x30, 0xc0, 0x04, 0x53, 0xf6, 0x15, 0x2f, 0xe1, 0x85, 0x3b, 0xd9, 0x40, 0x9b, 0x50, 0x07, 0x43, 0x7c, 0x36, 0x01, 0xa1, 0xda, 0x66, 0xc4, 0x20, 0x2f, 0x45, 0xc0, 0xcc, 0xb2, 0x64, 0x63, 0x9c, 0x07, 0x9d, 0x23, 0x27, 0x80, 0xa1, 0x7f, 0xc2, 0xe0, 0xa0, 0xfd, 0xc3, 0x98, 0x83, 0xa3, 0xaa, 0x6b, 0xdc, 0x9f, 0x6a, 0xc3, 0x32, 0x94, 0xf0, 0x80, 0xa0, 0xd9, 0xf1, 0x83, 0x41, 0x48, 0xa9, 0xb5, 0xed, 0x62, 0x50, 0x88, 0xec, 0x33, 0x32, 0xd2, 0x5f, 0xdc, 0xcc, 0xae, 0xc9, 0x74, 0xca, 0x0a, 0xab, 0x82, 0x06, 0x01, 0x46, 0x35, 0x30, 0xcd, 0x68, 0xec, 0x09, 0xab, 0x8c, 0xb0, 0x39, 0xe5, 0xd8, 0x5c, 0xa2, 0xe4, 0x82, 0xfe, 0x46, 0x01, 0xbd, 0xe7, 0x7f, 0x60, 0x1b, 0x50, 0x62, 0xfb, 0x6f, 0xee, 0x6c, 0xf1, 0xf7, 0x9b, 0xb7, 0x1c, 0x76, 0x48, 0xb5, 0xbe, 0xa5, 0x83, 0x73, 0x07, 0xa2, 0xe2, 0x73, 0xc7, 0x68, 0x34, 0xc8, 0xef, 0x12, 0xc4, 0x32, 0xdf, 0x37, 0x3d, 0xdc, 0x07, 0x0e, 0xa6, 0x92, 0x82, 0xbd, 0xba, 0x20, 0xc4, 0xb4, 0x8d, 0x1f, 0x19, 0x1c, 0x15, 0x0f, 0x5f, 0x01, 0xdf, 0x67, 0x1f, 0x59, 0xd1, 0x5e, 0x99, 0x60, 0xf6, 0xb8, 0x67, 0xe2, 0x46, 0x62, 0x11, 0x30, 0xfb, 0x81, 0x9d, 0x0b, 0xec, 0x36, 0xe9, 0x8d, 0x43, 0xfe, 0x55, 0xd9, 0x61, 0x98, 0x3f, 0x2e, 0x39, 0x6a, 0x26, 0x43, 0xb0, 0x6d, 0x08, 0xec, 0x2e, 0x42, 0x7e, 0x23, 0x82, 0x6f, 0xd9, 0xbb, 0xfd, 0x0a, 0xcd, 0x48, 0xe7, 0xd5, 0x8b, 0xa5, 0x80, 0x34, 0xca, 0x96, 0x4f, 0x58, 0x25, 0xba, 0x43, 0x5e, 0x3d, 0x1c, 0xee, 0x72, 0xb8, 0x35, 0x8c, 0x5d, 0xd9, 0x69, 0x24, 0x58, 0x36, 0x21, 0xb0, 0x45, 0xa4, 0xad, 0x40, 0xda, 0x94, 0x98, 0x0f, 0xb1, 0xed, 0x6c, 0xad, 0x26, 0x03, 0x04, 0x82, 0xff, 0x15, 0x00, 0x41, 0x87, 0x06, 0x93, 0xd4, 0x86, 0xa9, 0x7e, 0xb8, 0xd9, 0x70, 0x34, 0x6d, 0x8e, 0x6a, 0x16, 0xe2, 0x46, 0x52, 0xb0, 0x78, 0x54, 0x53, 0xaf}, /* This is a DHPart2 packet, sequence number is 0x02d1, SSRC 0x87654321 */ {0x10, 0x00, 0x02, 0xd1, 0x5a, 0x52, 0x54, 0x50, 0x87, 0x65, 0x43, 0x21, 0x50, 0x5a, 0x00, 0x75, 0x44, 0x48, 0x50, 0x61, 0x72, 0x74, 0x32, 0x20, 0x9e, 0xb2, 0xa5, 0x8b, 0xe8, 0x96, 0x37, 0xf5, 0x5a, 0x41, 0x34, 0xb2, 0xec, 0xda, 0x84, 0x95, 0xf0, 0xf8, 0x9b, 0xab, 0x61, 0x4f, 0x7c, 0x9e, 0x56, 0xb7, 0x3b, 0xd3, 0x46, 0xba, 0xbe, 0x9a, 0xae, 0x97, 0x97, 0xda, 0x5f, 0x9f, 0x89, 0xba, 0xfe, 0x61, 0x66, 0x5f, 0x9e, 0xa4, 0x5b, 0xcb, 0xd5, 0x69, 0xcf, 0xfb, 0xfd, 0xdc, 0xac, 0x79, 0xac, 0x1d, 0x0b, 0xe5, 0x15, 0x75, 0x39, 0x2e, 0xe5, 0xa9, 0x2a, 0x60, 0xd7, 0xe3, 0x48, 0xd0, 0x1f, 0xd8, 0x61, 0x65, 0xfd, 0x2e, 0x5c, 0xef, 0x43, 0xf5, 0x63, 0x99, 0xb3, 0x45, 0x25, 0xe3, 0xbc, 0xf0, 0xe1, 0xad, 0xf7, 0x84, 0xcd, 0x82, 0x20, 0xe3, 0x6f, 0x2c, 0x77, 0x51, 0xf1, 0x11, 0x2e, 0x4a, 0x2c, 0xfd, 0x2f, 0x5e, 0x74, 0xa9, 0x37, 0x99, 0xff, 0xf7, 0x4c, 0x2d, 0xa8, 0xcf, 0x51, 0xfd, 0x5b, 0xe7, 0x51, 0x14, 0x6d, 0xbc, 0x2f, 0x5b, 0xb9, 0x77, 0x85, 0xad, 0xb4, 0x72, 0x99, 0xad, 0x7b, 0x6c, 0x6a, 0xdf, 0x4d, 0xca, 0x2f, 0xef, 0x8b, 0x5e, 0x4b, 0xf3, 0xd9, 0xfd, 0xbd, 0x47, 0x1a, 0x72, 0xe2, 0x41, 0xd8, 0xfa, 0xa1, 0x25, 0x00, 0xa3, 0xfe, 0x12, 0xda, 0xf6, 0x16, 0xb3, 0xb3, 0x08, 0x02, 0xfd, 0x0a, 0x6a, 0xab, 0x85, 0x17, 0xd7, 0x0f, 0xf4, 0x6b, 0xdf, 0x8f, 0xe2, 0x05, 0xf4, 0x5b, 0x13, 0x26, 0xa9, 0xe2, 0x57, 0xb6, 0xda, 0x76, 0x17, 0x3c, 0x52, 0x13, 0x8d, 0x83, 0xc0, 0x2b, 0xe7, 0x2e, 0xbd, 0xb0, 0xde, 0x98, 0x4f, 0x7a, 0x95, 0xa1, 0x75, 0x19, 0x6e, 0xda, 0x19, 0xff, 0x7f, 0xdd, 0x70, 0x01, 0x12, 0x3c, 0x9e, 0xd7, 0xfe, 0xc3, 0x22, 0x39, 0xce, 0x4f, 0x86, 0xd8, 0x85, 0x40, 0x75, 0xd4, 0xfe, 0x93, 0x57, 0xbc, 0x9b, 0x01, 0xa4, 0x71, 0x35, 0x70, 0x9d, 0x62, 0x91, 0x47, 0x4e, 0x32, 0xa2, 0x76, 0x16, 0x06, 0xaf, 0xc7, 0xe3, 0xe5, 0xdc, 0x25, 0xac, 0xe7, 0x68, 0x25, 0x69, 0x0f, 0x97, 0x8d, 0x91, 0x32, 0x81, 0x23, 0xb1, 0x08, 0xf3, 0xa3, 0x2b, 0x3d, 0xfb, 0xcf, 0x99, 0x12, 0x0a, 0x59, 0xb9, 0xbb, 0x76, 0x16, 0x71, 0xa2, 0x0b, 0x0a, 0x5a, 0x6c, 0x37, 0x99, 0x9d, 0xe6, 0x3a, 0x05, 0x89, 0xf7, 0xc6, 0xfc, 0xf3, 0xfe, 0x36, 0x97, 0x77, 0x86, 0xe4, 0x7c, 0x48, 0x93, 0x0b, 0x26, 0x8e, 0x31, 0xe9, 0x22, 0xcc, 0xd3, 0xe0, 0x56, 0x29, 0xc8, 0x26, 0xe6, 0x1f, 0xa8, 0xb8, 0x93, 0x98, 0xec, 0xd9, 0x7c, 0x4a, 0x45, 0xd3, 0x71, 0x35, 0x9f, 0x14, 0xc1, 0x99, 0xd5, 0x21, 0x0b, 0xfa, 0x0f, 0xfb, 0x31, 0x7a, 0xa0, 0x70, 0x35, 0xb3, 0x9b, 0x1b, 0xe7, 0x65, 0xfd, 0xe3, 0x7d, 0x0b, 0xcc, 0x34, 0x4b, 0xf1, 0x5a, 0x9f, 0x19, 0xa4, 0x8f, 0xc8, 0x30, 0xf1, 0x87, 0x99, 0xc2, 0x75, 0x55, 0x2a, 0x34, 0xd7, 0x81, 0x9c, 0x54, 0x12, 0x82, 0x69, 0x5f, 0x8b, 0x01, 0xc1, 0x45, 0x95, 0xf5, 0xb1, 0x2d, 0x27, 0x0d, 0xa9, 0xc3, 0x93, 0x54, 0x2f, 0x57, 0x04, 0x7b, 0x20, 0xb7, 0xac, 0x33, 0x68, 0xb3, 0xef, 0x9a, 0x33, 0x95, 0x82, 0x9d, 0xfa, 0x2a, 0xb9, 0x88, 0x06, 0x04, 0x88, 0x51, 0x2c, 0x46, 0xdb, 0x83, 0xd7, 0x2f, 0xea, 0x1f, 0x0f, 0x24, 0xab, 0x03, 0xef, 0xb0, 0x61, 0xc7, 0x90, 0xdc, 0x78, 0x17, 0xf2, 0x9a, 0xab}, /* This is a confirm1 packet, sequence number is 0x09f6, SSRC 0x12345678 */ {0x10, 0x00, 0x09, 0xf6, 0x5a, 0x52, 0x54, 0x50, 0x12, 0x34, 0x56, 0x78, 0x50, 0x5a, 0x00, 0x13, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x31, 0xb1, 0x21, 0xee, 0xe2, 0x67, 0xa4, 0xfd, 0xa6, 0x94, 0x24, 0x9a, 0x60, 0xf0, 0x2e, 0x5e, 0xd9, 0x33, 0xe1, 0xd8, 0x41, 0x54, 0xa3, 0x7c, 0xea, 0xe9, 0x61, 0xae, 0xf9, 0x19, 0x0d, 0xb3, 0x68, 0x68, 0x9e, 0xf8, 0x1a, 0x18, 0x91, 0x87, 0xc5, 0x6e, 0x5e, 0x2d, 0x5e, 0x32, 0xa2, 0xb2, 0x66, 0x31, 0xb8, 0xe5, 0x59, 0xc9, 0x10, 0xbb, 0xa0, 0x00, 0x6c, 0xee, 0x0c, 0x6d, 0xfb, 0xeb, 0x32, 0x85, 0xb6, 0x6e, 0x93}, /* This is a confirm2 packet, sequence number is 0x02d2, SSRC 0x87654321 */ {0x10, 0x00, 0x02, 0xd2, 0x5a, 0x52, 0x54, 0x50, 0x87, 0x65, 0x43, 0x21, 0x50, 0x5a, 0x00, 0x13, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x32, 0x0f, 0xec, 0xfa, 0x4b, 0x45, 0x17, 0x9d, 0xb3, 0x92, 0x7d, 0x1c, 0x53, 0x86, 0x01, 0x12, 0xd9, 0x25, 0x48, 0xca, 0x18, 0xb9, 0x10, 0x95, 0x04, 0xb7, 0xc8, 0xee, 0x87, 0x2b, 0xec, 0x59, 0x39, 0x92, 0x96, 0x11, 0x73, 0xa6, 0x69, 0x2b, 0x11, 0xcd, 0x1d, 0xa1, 0x73, 0xb2, 0xc9, 0x29, 0x6f, 0x82, 0x32, 0x6a, 0x0a, 0x56, 0x40, 0x57, 0xfb, 0xac, 0xab, 0x20, 0xb8, 0xe2, 0xa9, 0x2c, 0x61, 0x6a, 0x05, 0xe8, 0xb5}, /* This is a conf2ACK packet, sequence number 0x09f7, SSRC 0x12345678 */ {0x10, 0x00, 0x09, 0xf7, 0x5a, 0x52, 0x54, 0x50, 0x12, 0x34, 0x56, 0x78, 0x50, 0x5a, 0x00, 0x03, 0x43, 0x6f, 0x6e, 0x66, 0x32, 0x41, 0x43, 0x4b, 0x23, 0xc1, 0x1b, 0x45}, }; /* Hash images for both sides */ uint8_t H12345678[4][32] = { {0xbb, 0xbf, 0x7e, 0xb1, 0x14, 0xd5, 0xd4, 0x0c, 0x6b, 0xb0, 0x79, 0x58, 0x19, 0xc1, 0xd0, 0x83, 0xc9, 0xe1, 0xf4, 0x2e, 0x11, 0xcd, 0x7e, 0xc3, 0xaa, 0xd8, 0xb9, 0x17, 0xe6, 0xb5, 0x9e, 0x86}, {0x28, 0x7c, 0x28, 0xe4, 0xd7, 0x3d, 0x14, 0x39, 0xb5, 0x6d, 0x1c, 0x47, 0x9d, 0x59, 0x0a, 0xf2, 0x10, 0x33, 0xde, 0x6b, 0xd5, 0x2b, 0xfb, 0x26, 0xa5, 0x87, 0x4d, 0xe9, 0x20, 0x6b, 0x9f, 0xdd}, {0x70, 0x12, 0xef, 0x2e, 0x85, 0x2f, 0xfc, 0x84, 0xb8, 0x8d, 0xcc, 0x03, 0xd7, 0x8f, 0x53, 0x01, 0x63, 0xfb, 0xd3, 0xb0, 0x2d, 0xbb, 0x9e, 0x98, 0x22, 0x0d, 0xe3, 0xe3, 0x64, 0x25, 0x04, 0x0f}, {0xe8, 0xd5, 0x26, 0xc1, 0x3a, 0x0c, 0x4c, 0x6a, 0xce, 0x18, 0xaa, 0xc7, 0xc4, 0xa4, 0x07, 0x0e, 0x65, 0x7a, 0x4d, 0xca, 0x78, 0xf2, 0xcc, 0xcd, 0x20, 0x50, 0x38, 0x73, 0xe9, 0x7e, 0x08, 0x29} }; uint8_t H87654321[4][32] = { {0x09, 0x02, 0xcc, 0x13, 0xc4, 0x84, 0x03, 0x31, 0x68, 0x91, 0x05, 0x4d, 0xe0, 0x6d, 0xf4, 0xc9, 0x6a, 0xb5, 0xbe, 0x82, 0xe8, 0x37, 0x33, 0xb7, 0xa9, 0xce, 0xbe, 0xb5, 0x42, 0xaa, 0x54, 0xba}, {0x9e, 0xb2, 0xa5, 0x8b, 0xe8, 0x96, 0x37, 0xf5, 0x5a, 0x41, 0x34, 0xb2, 0xec, 0xda, 0x84, 0x95, 0xf0, 0xf8, 0x9b, 0xab, 0x61, 0x4f, 0x7c, 0x9e, 0x56, 0xb7, 0x3b, 0xd3, 0x46, 0xba, 0xbe, 0x9a}, {0xd9, 0xff, 0x14, 0x8b, 0x34, 0xaa, 0x69, 0xe9, 0x33, 0xc1, 0x62, 0xe6, 0x6b, 0xe8, 0xcd, 0x9d, 0xe3, 0x0f, 0xb7, 0x6a, 0xe8, 0x6a, 0x62, 0x2b, 0xcb, 0xe4, 0x6b, 0x91, 0x05, 0xc7, 0xc8, 0x7e}, {0x8d, 0x0f, 0x5a, 0x20, 0x79, 0x97, 0x42, 0x01, 0x99, 0x45, 0x45, 0xf7, 0x0e, 0x31, 0x06, 0xe1, 0x05, 0xc0, 0xb9, 0x24, 0xe9, 0xc9, 0x78, 0xc7, 0x38, 0xf5, 0x97, 0x48, 0xef, 0x42, 0x6a, 0x3e} }; /* Hello packet and matching zrtp-hash generated using libzrtpcpp */ #define ZRTPHASHPATTERN "1.10 3be3a5d605f6bc51951f6eb151a6ecbc071a9a2f4273ddd4affe5215d7300815" #define ZRTPHASHPATTERN_WRONG "1.10 00e3a5d605f6bc51951f6eb151a6ecbc071a9a2f4273ddd4affe5215d7300815" uint8_t HelloPacketZrtpHash[168] = { 0x10, 0x00, 0x00, 0x01, 0x5a, 0x52, 0x54, 0x50, 0x76, 0xdc, 0xa7, 0xbd, 0x50, 0x5a, 0x00, 0x26, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x20, 0x20, 0x31, 0x2e, 0x31, 0x30, 0x50, 0x4a, 0x53, 0x20, 0x5a, 0x52, 0x54, 0x50, 0x20, 0x33, 0x2e, 0x30, 0x2e, 0x30, 0x20, 0x20, 0xe4, 0xba, 0x78, 0xba, 0xa0, 0x2a, 0xd4, 0x36, 0x1a, 0xb8, 0xd2, 0xdd, 0x57, 0x8a, 0x68, 0x16, 0xbb, 0xe4, 0x05, 0xb8, 0x69, 0xb7, 0xaf, 0x06, 0x4d, 0x1c, 0x09, 0x53, 0x1c, 0xeb, 0xc6, 0x29, 0x20, 0x12, 0xa2, 0xf0, 0x6c, 0xfd, 0x0f, 0xfb, 0x88, 0xd8, 0xe6, 0x52, 0x00, 0x02, 0x44, 0x51, 0x53, 0x33, 0x38, 0x34, 0x53, 0x32, 0x35, 0x36, 0x32, 0x46, 0x53, 0x33, 0x41, 0x45, 0x53, 0x33, 0x32, 0x46, 0x53, 0x31, 0x41, 0x45, 0x53, 0x31, 0x53, 0x4b, 0x33, 0x32, 0x53, 0x4b, 0x36, 0x34, 0x48, 0x53, 0x33, 0x32, 0x48, 0x53, 0x38, 0x30, 0x45, 0x43, 0x32, 0x35, 0x44, 0x48, 0x33, 0x6b, 0x45, 0x43, 0x33, 0x38, 0x44, 0x48, 0x32, 0x6b, 0x4d, 0x75, 0x6c, 0x74, 0x42, 0x33, 0x32, 0x20, 0x05, 0xc9, 0xf1, 0x87, 0x2b, 0x41, 0xa9, 0x9a, 0x0a, 0x1d, 0xcd, 0x3c}; /* mac and zrtp keys */ uint8_t mackeyi[32] = {0xdc, 0x47, 0xe1, 0xc7, 0x48, 0x11, 0xb1, 0x54, 0x14, 0x2a, 0x91, 0x29, 0x9f, 0xa4, 0x8b, 0x45, 0x87, 0x16, 0x8d, 0x3a, 0xe6, 0xb0, 0x0c, 0x08, 0x4f, 0xa5, 0x48, 0xd5, 0x96, 0x67, 0x1a, 0x1b}; uint8_t mackeyr[32] = {0x3a, 0xa5, 0x22, 0x43, 0x26, 0x13, 0x8f, 0xd6, 0x54, 0x59, 0x40, 0xb8, 0x5c, 0xf4, 0x0f, 0x0c, 0xbc, 0x9c, 0x4f, 0x7d, 0x55, 0xeb, 0x4b, 0xa5, 0x1e, 0x1c, 0x42, 0xd0, 0x5e, 0xac, 0x12, 0x06}; uint8_t zrtpkeyi[16] = {0x22, 0xf6, 0xea, 0xaa, 0xa4, 0xad, 0x53, 0x30, 0x71, 0x97, 0xcc, 0x68, 0x6b, 0xb0, 0xcb, 0x55}; uint8_t zrtpkeyr[16] = {0x09, 0x50, 0xcd, 0x9e, 0xc2, 0x78, 0x54, 0x31, 0x93, 0x2e, 0x99, 0x31, 0x15, 0x58, 0xd0, 0x2a}; void test_parser_param(uint8_t hvi_trick) { int i, retval; bzrtpPacket_t *zrtpPacket; /* Create zrtp Context to use H0-H3 chains and others */ bzrtpContext_t *context87654321 = bzrtp_createBzrtpContext(); bzrtpContext_t *context12345678 = bzrtp_createBzrtpContext(); bzrtp_initBzrtpContext(context87654321, 0x87654321); bzrtp_initBzrtpContext(context12345678, 0x12345678); /* replace created H by the patterns one to be able to generate the correct packet */ memcpy (context12345678->channelContext[0]->selfH[0], H12345678[0], 32); memcpy (context12345678->channelContext[0]->selfH[1], H12345678[1], 32); memcpy (context12345678->channelContext[0]->selfH[2], H12345678[2], 32); memcpy (context12345678->channelContext[0]->selfH[3], H12345678[3], 32); memcpy (context87654321->channelContext[0]->selfH[0], H87654321[0], 32); memcpy (context87654321->channelContext[0]->selfH[1], H87654321[1], 32); memcpy (context87654321->channelContext[0]->selfH[2], H87654321[2], 32); memcpy (context87654321->channelContext[0]->selfH[3], H87654321[3], 32); /* preset the key agreement algo in the contexts */ context87654321->channelContext[0]->keyAgreementAlgo = ZRTP_KEYAGREEMENT_DH3k; context12345678->channelContext[0]->keyAgreementAlgo = ZRTP_KEYAGREEMENT_DH3k; context87654321->channelContext[0]->cipherAlgo = ZRTP_CIPHER_AES1; context12345678->channelContext[0]->cipherAlgo = ZRTP_CIPHER_AES1; context87654321->channelContext[0]->hashAlgo = ZRTP_HASH_S256; context12345678->channelContext[0]->hashAlgo = ZRTP_HASH_S256; bzrtp_updateCryptoFunctionPointers(context87654321->channelContext[0]); bzrtp_updateCryptoFunctionPointers(context12345678->channelContext[0]); /* set the zrtp and mac keys */ context87654321->channelContext[0]->mackeyi = (uint8_t *)malloc(32); context12345678->channelContext[0]->mackeyi = (uint8_t *)malloc(32); context87654321->channelContext[0]->mackeyr = (uint8_t *)malloc(32); context12345678->channelContext[0]->mackeyr = (uint8_t *)malloc(32); context87654321->channelContext[0]->zrtpkeyi = (uint8_t *)malloc(16); context12345678->channelContext[0]->zrtpkeyi = (uint8_t *)malloc(16); context87654321->channelContext[0]->zrtpkeyr = (uint8_t *)malloc(16); context12345678->channelContext[0]->zrtpkeyr = (uint8_t *)malloc(16); memcpy(context12345678->channelContext[0]->mackeyi, mackeyi, 32); memcpy(context12345678->channelContext[0]->mackeyr, mackeyr, 32); memcpy(context12345678->channelContext[0]->zrtpkeyi, zrtpkeyi, 16); memcpy(context12345678->channelContext[0]->zrtpkeyr, zrtpkeyr, 16); memcpy(context87654321->channelContext[0]->mackeyi, mackeyi, 32); memcpy(context87654321->channelContext[0]->mackeyr, mackeyr, 32); memcpy(context87654321->channelContext[0]->zrtpkeyi, zrtpkeyi, 16); memcpy(context87654321->channelContext[0]->zrtpkeyr, zrtpkeyr, 16); /* set the role: 87654321 is initiator in our exchange pattern */ context12345678->channelContext[0]->role = BZRTP_ROLE_RESPONDER; /* set the peer hello packet Hash for context 12345678, the other one will be set after Hello Packet reception */ bzrtp_setPeerHelloHash(context12345678, 0x12345678, (uint8_t *)patternZRTPHelloHash87654321, strlen((const char *)patternZRTPHelloHash87654321)); for (i=0; ichannelContext[0]:context87654321->channelContext[0], patternZRTPPackets[i], patternZRTPMetaData[i][0], zrtpPacket); if (hvi_trick==0) { BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (retval != 0) goto error; } else { /* when hvi trick is enabled, the DH2 parsing shall fail and return BZRTP_PARSER_ERROR_UNMATCHINGHVI */ if (zrtpPacket->messageType==MSGTYPE_DHPART2) { BC_ASSERT_EQUAL(retval, BZRTP_PARSER_ERROR_UNMATCHINGHVI, int, "%d"); bzrtp_freeZrtpPacket(zrtpPacket); goto error; } else { BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (retval != 0) goto error; } } bzrtp_message("parsing Ret val is %x index is %d\n", retval, i); /* We must store some packets in the context if we want to be able to parse further packets */ /* Check also the zrtp hello hash */ if (zrtpPacket->messageType==MSGTYPE_HELLO) { if (patternZRTPMetaData[i][2]==0x87654321) { bzrtp_freeZrtpPacket(context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]); bzrtp_freeZrtpPacket(context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]); context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; } else { bzrtp_freeZrtpPacket(context87654321->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]); bzrtp_freeZrtpPacket(context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]); context87654321->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; /* Set the correct one */ retval = bzrtp_setPeerHelloHash(context87654321, 0x87654321, (uint8_t *)patternZRTPHelloHash12345678, strlen((const char *)patternZRTPHelloHash12345678)); BC_ASSERT_EQUAL(retval, 0, int, "%d"); if (retval != 0) goto error; } freePacketFlag = 0; } if (zrtpPacket->messageType==MSGTYPE_COMMIT) { if (patternZRTPMetaData[i][2]==0x87654321) { context12345678->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID] = zrtpPacket; } else { context87654321->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID] = zrtpPacket; } freePacketFlag = 0; } if (zrtpPacket->messageType==MSGTYPE_DHPART1 || zrtpPacket->messageType==MSGTYPE_DHPART2) { if (patternZRTPMetaData[i][2]==0x87654321) { context12345678->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID] = zrtpPacket; } else { context87654321->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID] = zrtpPacket; } freePacketFlag = 0; } /* free the packet string as will be created again by the packetBuild function and might have been copied by packetParser */ free(zrtpPacket->packetString); /* build a packet string from the parser packet*/ retval = bzrtp_packetBuild((patternZRTPMetaData[i][2]==0x12345678)?context12345678:context87654321, (patternZRTPMetaData[i][2]==0x12345678)?context12345678->channelContext[0]:context87654321->channelContext[0], zrtpPacket, patternZRTPMetaData[i][1]); /* if (retval ==0) { packetDump(zrtpPacket, 1); } else { bzrtp_message("Ret val is %x index is %d\n", retval, i); }*/ /* check they are the same */ if (zrtpPacket->packetString != NULL) { BC_ASSERT_TRUE(memcmp(zrtpPacket->packetString, patternZRTPPackets[i], patternZRTPMetaData[i][0]) == 0); } else { BC_FAIL("Unable to build packet"); } /* modify the hvi stored in the peerPackets, this shall result in parsing failure on DH2 packet */ if (hvi_trick == 1) { if (zrtpPacket->messageType==MSGTYPE_COMMIT) { if (patternZRTPMetaData[i][2]==0x87654321) { bzrtpCommitMessage_t *peerCommitMessageData; peerCommitMessageData = (bzrtpCommitMessage_t *)zrtpPacket->messageData; peerCommitMessageData->hvi[0]=0xFF; } } } if (freePacketFlag == 1) { bzrtp_freeZrtpPacket(zrtpPacket); } } error: /* reset pointers to selfHello packet in order to avoid double free */ context87654321->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL; context12345678->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID] = NULL; bzrtp_destroyBzrtpContext(context87654321, 0x87654321); bzrtp_destroyBzrtpContext(context12345678, 0x12345678); } static void test_parser(void) { test_parser_param(0); } static void test_parser_hvi(void) { test_parser_param(1); } /* context structure mainly used by statemachine test, but also needed by parserComplete to get the zid Filename */ typedef struct my_Context_struct { unsigned char nom[30]; /* nom du contexte */ bzrtpContext_t *peerContext; bzrtpChannelContext_t *peerChannelContext; } my_Context_t; static void test_parserComplete() { int retval; /* alice's maintained packet */ bzrtpPacket_t *alice_Hello, *alice_HelloFromBob, *alice_HelloACK, *alice_HelloACKFromBob; /* bob's maintained packet */ bzrtpPacket_t *bob_Hello, *bob_HelloFromAlice, *bob_HelloACK, *bob_HelloACKFromAlice; /* Create zrtp Context */ bzrtpContext_t *contextAlice = bzrtp_createBzrtpContext(); bzrtpContext_t *contextBob = bzrtp_createBzrtpContext(); bzrtpHelloMessage_t *alice_HelloFromBob_message; bzrtpHelloMessage_t *bob_HelloFromAlice_message; bzrtpPacket_t *alice_selfDHPart; bzrtpPacket_t *bob_selfDHPart; bzrtpPacket_t *alice_Commit; bzrtpPacket_t *bob_Commit; bzrtpPacket_t *bob_CommitFromAlice; bzrtpPacket_t *alice_CommitFromBob; uint8_t tmpBuffer[8]; bzrtpDHPartMessage_t *bob_DHPart1; bzrtpPacket_t *alice_DHPart1FromBob; bzrtpDHPartMessage_t *alice_DHPart1FromBob_message=NULL; bzrtpPacket_t *bob_DHPart2FromAlice; bzrtpDHPartMessage_t *bob_DHPart2FromAlice_message=NULL; uint16_t secretLength; uint16_t totalHashDataLength; uint8_t *dataToHash; uint16_t hashDataIndex = 0; uint8_t alice_totalHash[32]; /* Note : actual length of hash depends on the choosen algo */ uint8_t bob_totalHash[32]; /* Note : actual length of hash depends on the choosen algo */ uint8_t *s1=NULL; uint32_t s1Length=0; uint8_t *s2=NULL; uint32_t s2Length=0; uint8_t *s3=NULL; uint32_t s3Length=0; uint8_t alice_sasHash[32]; uint8_t bob_sasHash[32]; uint32_t sasValue; char sas[32]; bzrtpPacket_t *bob_Confirm1; bzrtpPacket_t *alice_Confirm1FromBob; bzrtpConfirmMessage_t *alice_Confirm1FromBob_message=NULL; bzrtpPacket_t *alice_Confirm2; bzrtpPacket_t *bob_Confirm2FromAlice; bzrtpConfirmMessage_t *bob_Confirm2FromAlice_message=NULL; bzrtpPacket_t *bob_Conf2ACK; bzrtpPacket_t *alice_Conf2ACKFromBob; bzrtpPacket_t *alice_Confirm1; bzrtpPacket_t *bob_Confirm1FromAlice; bzrtpConfirmMessage_t *bob_Confirm1FromAlice_message=NULL; bzrtpPacket_t *bob_Confirm2; bzrtpPacket_t *alice_Confirm2FromBob; bzrtpConfirmMessage_t *alice_Confirm2FromBob_message=NULL; bzrtpPacket_t *alice_Conf2ACK; bzrtpPacket_t *bob_Conf2ACKFromAlice; bzrtp_message ("Init the contexts\n"); /* end the context init */ bzrtp_initBzrtpContext(contextAlice, 0x12345678);/* Alice's SSRC of main channel is 12345678 */ bzrtp_initBzrtpContext(contextBob, 0x87654321); /* Bob's SSRC of main channel is 87654321 */ /* Hello packets are built during the context init(but we must still increase their sequence Number) */ alice_Hello = contextAlice->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]; bob_Hello = contextBob->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]; contextAlice->channelContext[0]->selfSequenceNumber++; contextBob->channelContext[0]->selfSequenceNumber++; /* now send Alice Hello's to Bob and vice-versa, so they parse them */ alice_HelloFromBob = bzrtp_packetCheck(bob_Hello->packetString, bob_Hello->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_Hello->packetString, bob_Hello->messageLength+16, alice_HelloFromBob); bzrtp_message ("Alice parsing returns %x\n", retval); if (retval==0) { bzrtpHelloMessage_t *alice_HelloFromBob_message; int i; contextAlice->channelContext[0]->peerSequenceNumber = alice_HelloFromBob->sequenceNumber; /* save bob's Hello packet in Alice's context */ contextAlice->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = alice_HelloFromBob; /* determine crypto Algo to use */ alice_HelloFromBob_message = (bzrtpHelloMessage_t *)alice_HelloFromBob->messageData; retval = bzrtp_cryptoAlgoAgreement(contextAlice, contextAlice->channelContext[0], contextAlice->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData); if (retval == 0) { bzrtp_message ("Alice selected algo %x\n", contextAlice->channelContext[0]->keyAgreementAlgo); memcpy(contextAlice->peerZID, alice_HelloFromBob_message->ZID, 12); } /* check if the peer accept MultiChannel */ for (i=0; ikc; i++) { if (alice_HelloFromBob_message->supportedKeyAgreement[i] == ZRTP_KEYAGREEMENT_Mult) { contextAlice->peerSupportMultiChannel = 1; } } } bob_HelloFromAlice = bzrtp_packetCheck(alice_Hello->packetString, alice_Hello->messageLength+16, contextBob->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[0], alice_Hello->packetString, alice_Hello->messageLength+16, bob_HelloFromAlice); bzrtp_message ("Bob parsing returns %x\n", retval); if (retval==0) { bzrtpHelloMessage_t *bob_HelloFromAlice_message; int i; contextBob->channelContext[0]->peerSequenceNumber = bob_HelloFromAlice->sequenceNumber; /* save alice's Hello packet in bob's context */ contextBob->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = bob_HelloFromAlice; /* determine crypto Algo to use */ bob_HelloFromAlice_message = (bzrtpHelloMessage_t *)bob_HelloFromAlice->messageData; retval = bzrtp_cryptoAlgoAgreement(contextBob, contextBob->channelContext[0], contextBob->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageData); if (retval == 0) { bzrtp_message ("Bob selected algo %x\n", contextBob->channelContext[0]->keyAgreementAlgo); memcpy(contextBob->peerZID, bob_HelloFromAlice_message->ZID, 12); } /* check if the peer accept MultiChannel */ for (i=0; ikc; i++) { if (bob_HelloFromAlice_message->supportedKeyAgreement[i] == ZRTP_KEYAGREEMENT_Mult) { contextBob->peerSupportMultiChannel = 1; } } } /* update context with hello message information : H3 and compute initiator and responder's shared secret Hashs */ alice_HelloFromBob_message = (bzrtpHelloMessage_t *)alice_HelloFromBob->messageData; memcpy(contextAlice->channelContext[0]->peerH[3], alice_HelloFromBob_message->H3, 32); bob_HelloFromAlice_message = (bzrtpHelloMessage_t *)bob_HelloFromAlice->messageData; memcpy(contextBob->channelContext[0]->peerH[3], bob_HelloFromAlice_message->H3, 32); /* get the secrets associated to peer ZID */ bzrtp_getPeerAssociatedSecrets(contextAlice, alice_HelloFromBob_message->ZID); bzrtp_getPeerAssociatedSecrets(contextBob, bob_HelloFromAlice_message->ZID); /* compute the initiator hashed secret as in rfc section 4.3.1 */ if (contextAlice->cachedSecret.rs1!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.rs1, contextAlice->cachedSecret.rs1Length, (uint8_t *)"Initiator", 9, 8, contextAlice->initiatorCachedSecretHash.rs1ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->initiatorCachedSecretHash.rs1ID, 8); } if (contextAlice->cachedSecret.rs2!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.rs2, contextAlice->cachedSecret.rs2Length, (uint8_t *)"Initiator", 9, 8, contextAlice->initiatorCachedSecretHash.rs2ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->initiatorCachedSecretHash.rs2ID, 8); } if (contextAlice->cachedSecret.auxsecret!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.auxsecret, contextAlice->cachedSecret.auxsecretLength, contextAlice->channelContext[0]->selfH[3], 32, 8, contextAlice->channelContext[0]->initiatorAuxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->channelContext[0]->initiatorAuxsecretID, 8); } if (contextAlice->cachedSecret.pbxsecret!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.pbxsecret, contextAlice->cachedSecret.pbxsecretLength, (uint8_t *)"Initiator", 9, 8, contextAlice->initiatorCachedSecretHash.pbxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->initiatorCachedSecretHash.pbxsecretID, 8); } if (contextAlice->cachedSecret.rs1!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.rs1, contextAlice->cachedSecret.rs1Length, (uint8_t *)"Responder", 9, 8, contextAlice->responderCachedSecretHash.rs1ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->responderCachedSecretHash.rs1ID, 8); } if (contextAlice->cachedSecret.rs2!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.rs2, contextAlice->cachedSecret.rs2Length, (uint8_t *)"Responder", 9, 8, contextAlice->responderCachedSecretHash.rs2ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->responderCachedSecretHash.rs2ID, 8); } if (contextAlice->cachedSecret.auxsecret!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.auxsecret, contextAlice->cachedSecret.auxsecretLength, contextAlice->channelContext[0]->peerH[3], 32, 8, contextAlice->channelContext[0]->responderAuxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->channelContext[0]->responderAuxsecretID, 8); } if (contextAlice->cachedSecret.pbxsecret!=NULL) { contextAlice->channelContext[0]->hmacFunction(contextAlice->cachedSecret.pbxsecret, contextAlice->cachedSecret.pbxsecretLength, (uint8_t *)"Responder", 9, 8, contextAlice->responderCachedSecretHash.pbxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextAlice->RNGContext, contextAlice->responderCachedSecretHash.pbxsecretID, 8); } /* Bob hashes*/ if (contextBob->cachedSecret.rs1!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.rs1, contextBob->cachedSecret.rs1Length, (uint8_t *)"Initiator", 9, 8, contextBob->initiatorCachedSecretHash.rs1ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->initiatorCachedSecretHash.rs1ID, 8); } if (contextBob->cachedSecret.rs2!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.rs2, contextBob->cachedSecret.rs2Length, (uint8_t *)"Initiator", 9, 8, contextBob->initiatorCachedSecretHash.rs2ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->initiatorCachedSecretHash.rs2ID, 8); } if (contextBob->cachedSecret.auxsecret!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.auxsecret, contextBob->cachedSecret.auxsecretLength, contextBob->channelContext[0]->selfH[3], 32, 8, contextBob->channelContext[0]->initiatorAuxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->channelContext[0]->initiatorAuxsecretID, 8); } if (contextBob->cachedSecret.pbxsecret!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.pbxsecret, contextBob->cachedSecret.pbxsecretLength, (uint8_t *)"Initiator", 9, 8, contextBob->initiatorCachedSecretHash.pbxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->initiatorCachedSecretHash.pbxsecretID, 8); } if (contextBob->cachedSecret.rs1!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.rs1, contextBob->cachedSecret.rs1Length, (uint8_t *)"Responder", 9, 8, contextBob->responderCachedSecretHash.rs1ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->responderCachedSecretHash.rs1ID, 8); } if (contextBob->cachedSecret.rs2!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.rs2, contextBob->cachedSecret.rs2Length, (uint8_t *)"Responder", 9, 8, contextBob->responderCachedSecretHash.rs2ID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->responderCachedSecretHash.rs2ID, 8); } if (contextBob->cachedSecret.auxsecret!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.auxsecret, contextBob->cachedSecret.auxsecretLength, contextBob->channelContext[0]->peerH[3], 32, 8, contextBob->channelContext[0]->responderAuxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->channelContext[0]->responderAuxsecretID, 8); } if (contextBob->cachedSecret.pbxsecret!=NULL) { contextBob->channelContext[0]->hmacFunction(contextBob->cachedSecret.pbxsecret, contextBob->cachedSecret.pbxsecretLength, (uint8_t *)"Responder", 9, 8, contextBob->responderCachedSecretHash.pbxsecretID); } else { /* we have no secret, generate a random */ bctbx_rng_get(contextBob->RNGContext, contextBob->responderCachedSecretHash.pbxsecretID, 8); } /* dump alice's packet on both sides */ bzrtp_message ("\nAlice original Packet is \n"); packetDump(alice_Hello, 1); bzrtp_message ("\nBob's parsed Alice Packet is \n"); packetDump(bob_HelloFromAlice, 0); /* Create the DHPart2 packet (that we then may change to DHPart1 if we ended to be the responder) */ alice_selfDHPart = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[0], MSGTYPE_DHPART2, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[0], alice_selfDHPart, 0); /* we don't care now about sequence number as we just need to build the message to be able to insert a hash of it into the commit packet */ if (retval == 0) { /* ok, insert it in context as we need it to build the commit packet */ contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID] = alice_selfDHPart; } else { bzrtp_message ("Alice building DHPart packet returns %x\n", retval); } bob_selfDHPart = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[0], MSGTYPE_DHPART2, &retval); retval +=bzrtp_packetBuild(contextBob, contextBob->channelContext[0], bob_selfDHPart, 0); /* we don't care now about sequence number as we just need to build the message to be able to insert a hash of it into the commit packet */ if (retval == 0) { /* ok, insert it in context as we need it to build the commit packet */ contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID] = bob_selfDHPart; } else { bzrtp_message ("Bob building DHPart packet returns %x\n", retval); } bzrtp_message("Alice DHPart packet:\n"); packetDump(alice_selfDHPart,0); bzrtp_message("Bob DHPart packet:\n"); packetDump(bob_selfDHPart,0); /* respond to HELLO packet with an HelloACK - 1 create packets */ alice_HelloACK = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[0], MSGTYPE_HELLOACK, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[0], alice_HelloACK, contextAlice->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextAlice->channelContext[0]->selfSequenceNumber++; } else { bzrtp_message("Alice building HelloACK return %x\n", retval); } bob_HelloACK = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[0], MSGTYPE_HELLOACK, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[0], bob_HelloACK, contextBob->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[0]->selfSequenceNumber++; } else { bzrtp_message("Bob building HelloACK return %x\n", retval); } /* exchange the HelloACK */ alice_HelloACKFromBob = bzrtp_packetCheck(bob_HelloACK->packetString, bob_HelloACK->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_HelloACK->packetString, bob_HelloACK->messageLength+16, alice_HelloACKFromBob); bzrtp_message ("Alice parsing Hello ACK returns %x\n", retval); if (retval==0) { contextAlice->channelContext[0]->peerSequenceNumber = alice_HelloACKFromBob->sequenceNumber; } bob_HelloACKFromAlice = bzrtp_packetCheck(alice_HelloACK->packetString, alice_HelloACK->messageLength+16, contextBob->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[0], alice_HelloACK->packetString, alice_HelloACK->messageLength+16, bob_HelloACKFromAlice); bzrtp_message ("Bob parsing Hello ACK returns %x\n", retval); if (retval==0) { contextBob->channelContext[0]->peerSequenceNumber = bob_HelloACKFromAlice->sequenceNumber; } bzrtp_freeZrtpPacket(alice_HelloACK); bzrtp_freeZrtpPacket(bob_HelloACK); bzrtp_freeZrtpPacket(alice_HelloACKFromBob); bzrtp_freeZrtpPacket(bob_HelloACKFromAlice); /* now build the commit message (both Alice and Bob will send it, then use the mechanism of rfc section 4.2 to determine who will be the initiator)*/ alice_Commit = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[0], MSGTYPE_COMMIT, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[0], alice_Commit, contextAlice->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextAlice->channelContext[0]->selfSequenceNumber++; contextAlice->channelContext[0]->selfPackets[COMMIT_MESSAGE_STORE_ID] = alice_Commit; } bzrtp_message("Alice building Commit return %x\n", retval); bob_Commit = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[0], MSGTYPE_COMMIT, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[0], bob_Commit, contextBob->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[0]->selfSequenceNumber++; contextBob->channelContext[0]->selfPackets[COMMIT_MESSAGE_STORE_ID] = bob_Commit; } bzrtp_message("Bob building Commit return %x\n", retval); /* and exchange the commits */ bob_CommitFromAlice = bzrtp_packetCheck(alice_Commit->packetString, alice_Commit->messageLength+16, contextBob->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[0], alice_Commit->packetString, alice_Commit->messageLength+16, bob_CommitFromAlice); bzrtp_message ("Bob parsing Commit returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ bzrtpCommitMessage_t *bob_CommitFromAlice_message = (bzrtpCommitMessage_t *)bob_CommitFromAlice->messageData; contextBob->channelContext[0]->peerSequenceNumber = bob_CommitFromAlice->sequenceNumber; memcpy(contextBob->channelContext[0]->peerH[2], bob_CommitFromAlice_message->H2, 32); contextBob->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID] = bob_CommitFromAlice; } packetDump(bob_CommitFromAlice, 0); alice_CommitFromBob = bzrtp_packetCheck(bob_Commit->packetString, bob_Commit->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_Commit->packetString, bob_Commit->messageLength+16, alice_CommitFromBob); bzrtp_message ("Alice parsing Commit returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextAlice->channelContext[0]->peerSequenceNumber = alice_CommitFromBob->sequenceNumber; /* Alice will be the initiator (commit contention not implemented in this test) so just discard bob's commit */ /*bzrtpCommirMessage_t *alice_CommitFromBob_message = (bzrtpCommitMessage_t *)alice_CommitFromBob->messageData; memcpy(contextAlice->channelContext[0]->peerH[2], alice_CommitFromBob_message->H2, 32); contextAlice->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID] = alice_CommitFromBob;*/ } packetDump(alice_CommitFromBob, 0); bzrtp_freeZrtpPacket(alice_CommitFromBob); /* Now determine who shall be the initiator : rfc section 4.2 */ /* select the one with the lowest value of hvi */ /* for test purpose, we will set Alice as the initiator */ contextBob->channelContext[0]->role = BZRTP_ROLE_RESPONDER; /* Bob (responder) shall update his selected algo list to match Alice selection */ /* no need to do this here as we have the same selection */ /* Bob is the responder, rebuild his DHPart packet to be responder and not initiator : */ /* as responder, bob must also swap his aux shared secret between responder and initiator as they are computed using the H3 and not a constant string */ memcpy(tmpBuffer, contextBob->channelContext[0]->initiatorAuxsecretID, 8); memcpy(contextBob->channelContext[0]->initiatorAuxsecretID, contextBob->channelContext[0]->responderAuxsecretID, 8); memcpy(contextBob->channelContext[0]->responderAuxsecretID, tmpBuffer, 8); contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageType = MSGTYPE_DHPART1; /* we are now part 1*/ bob_DHPart1 = (bzrtpDHPartMessage_t *)contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageData; /* change the shared secret ID to the responder one (we set them by default to the initiator's one) */ memcpy(bob_DHPart1->rs1ID, contextBob->responderCachedSecretHash.rs1ID, 8); memcpy(bob_DHPart1->rs2ID, contextBob->responderCachedSecretHash.rs2ID, 8); memcpy(bob_DHPart1->auxsecretID, contextBob->channelContext[0]->responderAuxsecretID, 8); memcpy(bob_DHPart1->pbxsecretID, contextBob->responderCachedSecretHash.pbxsecretID, 8); free(contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString); retval +=bzrtp_packetBuild(contextBob, contextBob->channelContext[0], contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID],contextBob->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[0]->selfSequenceNumber++; } bzrtp_message("Bob building DHPart1 return %x\n", retval); /* Alice parse bob's DHPart1 message */ alice_DHPart1FromBob = bzrtp_packetCheck(contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+16, alice_DHPart1FromBob); bzrtp_message ("Alice parsing DHPart1 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextAlice->channelContext[0]->peerSequenceNumber = alice_DHPart1FromBob->sequenceNumber; alice_DHPart1FromBob_message = (bzrtpDHPartMessage_t *)alice_DHPart1FromBob->messageData; memcpy(contextAlice->channelContext[0]->peerH[1], alice_DHPart1FromBob_message->H1, 32); contextAlice->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID] = alice_DHPart1FromBob; } packetDump(alice_DHPart1FromBob, 1); /* Now Alice may check which shared secret she expected and if they are valid in bob's DHPart1 */ if (contextAlice->cachedSecret.rs1!=NULL) { if (memcmp(contextAlice->responderCachedSecretHash.rs1ID, alice_DHPart1FromBob_message->rs1ID,8) != 0) { bzrtp_message ("Alice found that requested shared secret rs1 ID differs!\n"); } else { bzrtp_message("Alice validate rs1ID from bob DHPart1\n"); } } if (contextAlice->cachedSecret.rs2!=NULL) { if (memcmp(contextAlice->responderCachedSecretHash.rs2ID, alice_DHPart1FromBob_message->rs2ID,8) != 0) { bzrtp_message ("Alice found that requested shared secret rs2 ID differs!\n"); } else { bzrtp_message("Alice validate rs2ID from bob DHPart1\n"); } } if (contextAlice->cachedSecret.auxsecret!=NULL) { if (memcmp(contextAlice->channelContext[0]->responderAuxsecretID, alice_DHPart1FromBob_message->auxsecretID,8) != 0) { bzrtp_message ("Alice found that requested shared secret aux secret ID differs!\n"); } else { bzrtp_message("Alice validate aux secret ID from bob DHPart1\n"); } } if (contextAlice->cachedSecret.pbxsecret!=NULL) { if (memcmp(contextAlice->responderCachedSecretHash.pbxsecretID, alice_DHPart1FromBob_message->pbxsecretID,8) != 0) { bzrtp_message ("Alice found that requested shared secret pbx secret ID differs!\n"); } else { bzrtp_message("Alice validate pbxsecretID from bob DHPart1\n"); } } /* Now Alice shall check that the PV from bob is not 1 or Prime-1 TODO*/ /* Compute the shared DH secret */ if (contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(contextAlice->keyAgreementContext); DHMContext->peer = (uint8_t *)malloc(contextAlice->channelContext[0]->keyAgreementLength*sizeof(uint8_t)); memcpy (DHMContext->peer, alice_DHPart1FromBob_message->pv, contextAlice->channelContext[0]->keyAgreementLength); bctbx_DHMComputeSecret(DHMContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)contextAlice->RNGContext); } if (contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(contextAlice->keyAgreementContext); ECDHContext->peerPublic = (uint8_t *)malloc(contextAlice->channelContext[0]->keyAgreementLength*sizeof(uint8_t)); memcpy (ECDHContext->peerPublic, alice_DHPart1FromBob_message->pv, contextAlice->channelContext[0]->keyAgreementLength); bctbx_ECDHComputeSecret(ECDHContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)contextAlice->RNGContext); } /* So Alice send bob her DHPart2 message which is already prepared and stored (we just need to update the sequence number) */ bzrtp_packetUpdateSequenceNumber(contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID], contextAlice->channelContext[0]->selfSequenceNumber); contextAlice->channelContext[0]->selfSequenceNumber++; /* Bob parse Alice's DHPart2 message */ bob_DHPart2FromAlice = bzrtp_packetCheck(contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+16, contextBob->channelContext[0]->peerSequenceNumber, &retval); bzrtp_message ("Bob checking DHPart2 returns %x\n", retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[0], contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString, contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength+16, bob_DHPart2FromAlice); bzrtp_message ("Bob parsing DHPart2 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextBob->channelContext[0]->peerSequenceNumber = bob_DHPart2FromAlice->sequenceNumber; bob_DHPart2FromAlice_message = (bzrtpDHPartMessage_t *)bob_DHPart2FromAlice->messageData; memcpy(contextBob->channelContext[0]->peerH[1], bob_DHPart2FromAlice_message->H1, 32); contextBob->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID] = bob_DHPart2FromAlice; } packetDump(bob_DHPart2FromAlice, 0); /* Now Bob may check which shared secret she expected and if they are valid in bob's DHPart1 */ if (contextBob->cachedSecret.rs1!=NULL) { if (memcmp(contextBob->initiatorCachedSecretHash.rs1ID, bob_DHPart2FromAlice_message->rs1ID,8) != 0) { bzrtp_message ("Bob found that requested shared secret rs1 ID differs!\n"); } else { bzrtp_message("Bob validate rs1ID from Alice DHPart2\n"); } } if (contextBob->cachedSecret.rs2!=NULL) { if (memcmp(contextBob->initiatorCachedSecretHash.rs2ID, bob_DHPart2FromAlice_message->rs2ID,8) != 0) { bzrtp_message ("Bob found that requested shared secret rs2 ID differs!\n"); } else { bzrtp_message("Bob validate rs2ID from Alice DHPart2\n"); } } if (contextBob->cachedSecret.auxsecret!=NULL) { if (memcmp(contextBob->channelContext[0]->initiatorAuxsecretID, bob_DHPart2FromAlice_message->auxsecretID,8) != 0) { bzrtp_message ("Bob found that requested shared secret aux secret ID differs!\n"); } else { bzrtp_message("Bob validate aux secret ID from Alice DHPart2\n"); } } if (contextBob->cachedSecret.pbxsecret!=NULL) { if (memcmp(contextBob->initiatorCachedSecretHash.pbxsecretID, bob_DHPart2FromAlice_message->pbxsecretID,8) != 0) { bzrtp_message ("Bob found that requested shared secret pbx secret ID differs!\n"); } else { bzrtp_message("Bob validate pbxsecretID from Alice DHPart2\n"); } } /* Now Bob shall check that the PV from Alice is not 1 or Prime-1 TODO*/ /* Compute the shared DH secret */ if (contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(contextBob->keyAgreementContext); DHMContext->peer = (uint8_t *)malloc(contextBob->channelContext[0]->keyAgreementLength*sizeof(uint8_t)); memcpy (DHMContext->peer, bob_DHPart2FromAlice_message->pv, contextBob->channelContext[0]->keyAgreementLength); bctbx_DHMComputeSecret(DHMContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)contextAlice->RNGContext); } if (contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(contextBob->keyAgreementContext); ECDHContext->peerPublic = (uint8_t *)malloc(contextBob->channelContext[0]->keyAgreementLength*sizeof(uint8_t)); memcpy (ECDHContext->peerPublic, bob_DHPart2FromAlice_message->pv, contextBob->channelContext[0]->keyAgreementLength); bctbx_ECDHComputeSecret(ECDHContext, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, (void *)contextAlice->RNGContext); } /* JUST FOR TEST: check that the generated secrets are the same */ secretLength = bob_DHPart2FromAlice->messageLength-84; /* length of generated secret is the same than public value */ if (contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContextAlice = (bctbx_DHMContext_t *)(contextAlice->keyAgreementContext); bctbx_DHMContext_t *DHMContextBob = (bctbx_DHMContext_t *)(contextBob->keyAgreementContext); if (memcmp(DHMContextBob->key, DHMContextAlice->key, secretLength)==0) { bzrtp_message("Secret Key correctly exchanged \n"); BC_PASS("Secret Key exchange OK"); } else { BC_FAIL("Secret Key exchange failed"); bzrtp_message("ERROR : secretKey exchange failed!!\n"); } } if (contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContextAlice = (bctbx_ECDHContext_t *)(contextAlice->keyAgreementContext); bctbx_ECDHContext_t *ECDHContextBob = (bctbx_ECDHContext_t *)(contextBob->keyAgreementContext); if (memcmp(ECDHContextBob->sharedSecret, ECDHContextAlice->sharedSecret, secretLength)==0) { bzrtp_message("Secret Key correctly exchanged \n"); BC_PASS("Secret Key exchange OK"); } else { BC_FAIL("Secret Key exchange failed"); bzrtp_message("ERROR : secretKey exchange failed!!\n"); } } /* now compute the total_hash as in rfc section 4.4.1.4 * total_hash = hash(Hello of responder || Commit || DHPart1 || DHPart2) */ totalHashDataLength = bob_Hello->messageLength + alice_Commit->messageLength + contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength + alice_selfDHPart->messageLength; dataToHash = (uint8_t *)malloc(totalHashDataLength*sizeof(uint8_t)); /* get all data from Alice */ memcpy(dataToHash, contextAlice->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextAlice->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextAlice->channelContext[0]->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[0]->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextAlice->channelContext[0]->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextAlice->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextAlice->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength); contextAlice->channelContext[0]->hashFunction(dataToHash, totalHashDataLength, 32, alice_totalHash); /* get all data from Bob */ hashDataIndex = 0; memcpy(dataToHash, contextBob->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextBob->channelContext[0]->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextBob->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextBob->channelContext[0]->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextBob->channelContext[0]->selfPackets[DHPART_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextBob->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[0]->peerPackets[DHPART_MESSAGE_STORE_ID]->messageLength); contextBob->channelContext[0]->hashFunction(dataToHash, totalHashDataLength, 32, bob_totalHash); if (memcmp(bob_totalHash, alice_totalHash, 32) == 0) { bzrtp_message("Got the same total hash\n"); BC_PASS("Total Hash match"); } else { bzrtp_message("AARGG!! total hash mismatch"); BC_FAIL("Total Hash mismatch"); } /* now compute s0 and KDF_context as in rfc section 4.4.1.4 s0 = hash(counter || DHResult || "ZRTP-HMAC-KDF" || ZIDi || ZIDr || total_hash || len(s1) || s1 || len(s2) || s2 || len(s3) || s3) counter is a fixed 32 bits integer in big endian set to 1 : 0x00000001 */ free(dataToHash); contextAlice->channelContext[0]->KDFContextLength = 24+32;/* actual depends on selected hash length*/ contextAlice->channelContext[0]->KDFContext = (uint8_t *)malloc(contextAlice->channelContext[0]->KDFContextLength*sizeof(uint8_t)); memcpy(contextAlice->channelContext[0]->KDFContext, contextAlice->selfZID, 12); /* ZIDi*/ memcpy(contextAlice->channelContext[0]->KDFContext+12, contextAlice->peerZID, 12); /* ZIDr */ memcpy(contextAlice->channelContext[0]->KDFContext+24, alice_totalHash, 32); /* total Hash*/ /* get s1 from rs1 or rs2 */ if (contextAlice->cachedSecret.rs1 != NULL) { /* if there is a s1 (already validated when received the DHpacket) */ s1 = contextAlice->cachedSecret.rs1; s1Length = contextAlice->cachedSecret.rs1Length; } else if (contextAlice->cachedSecret.rs2 != NULL) { /* otherwise if there is a s2 (already validated when received the DHpacket) */ s1 = contextAlice->cachedSecret.rs2; s1Length = contextAlice->cachedSecret.rs2Length; } /* s2 is the auxsecret */ s2 = contextAlice->cachedSecret.auxsecret; /* this may be null if no match or no aux secret where found */ s2Length = contextAlice->cachedSecret.auxsecretLength; /* this may be 0 if no match or no aux secret where found */ /* s3 is the pbxsecret */ s3 = contextAlice->cachedSecret.pbxsecret; /* this may be null if no match or no pbx secret where found */ s3Length = contextAlice->cachedSecret.pbxsecretLength; /* this may be 0 if no match or no pbx secret where found */ totalHashDataLength = 4+secretLength+13/*ZRTP-HMAC-KDF string*/ + 12 + 12 + 32 + 4 +s1Length + 4 +s2Length + 4 + s3Length; /* secret length was computed before as the length of DH secret data */ dataToHash = (uint8_t *)malloc(totalHashDataLength*sizeof(uint8_t)); dataToHash[0] = 0x00; dataToHash[1] = 0x00; dataToHash[2] = 0x00; dataToHash[3] = 0x01; hashDataIndex = 4; if (contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(contextAlice->keyAgreementContext); memcpy(dataToHash+hashDataIndex, DHMContext->key, secretLength); } if (contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || contextAlice->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(contextAlice->keyAgreementContext); memcpy(dataToHash+hashDataIndex, ECDHContext->sharedSecret, secretLength); } hashDataIndex += secretLength; memcpy(dataToHash+hashDataIndex, "ZRTP-HMAC-KDF", 13); hashDataIndex += 13; memcpy(dataToHash+hashDataIndex, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength); hashDataIndex += 56; dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s1Length&0xFF); if (s1!=NULL) { memcpy(dataToHash+hashDataIndex, s1, s1Length); hashDataIndex += s1Length; } dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s2Length&0xFF); if (s2!=NULL) { memcpy(dataToHash+hashDataIndex, s2, s2Length); hashDataIndex += s2Length; } dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s3Length&0xFF); if (s3!=NULL) { memcpy(dataToHash+hashDataIndex, s3, s3Length); hashDataIndex += s3Length; } contextAlice->channelContext[0]->s0 = (uint8_t *)malloc(32*sizeof(uint8_t)); contextAlice->channelContext[0]->hashFunction(dataToHash, totalHashDataLength, 32, contextAlice->channelContext[0]->s0); /* destroy all cached keys in context */ if (contextAlice->cachedSecret.rs1!=NULL) { bzrtp_DestroyKey(contextAlice->cachedSecret.rs1, contextAlice->cachedSecret.rs1Length, contextAlice->RNGContext); free(contextAlice->cachedSecret.rs1); contextAlice->cachedSecret.rs1 = NULL; } if (contextAlice->cachedSecret.rs2!=NULL) { bzrtp_DestroyKey(contextAlice->cachedSecret.rs2, contextAlice->cachedSecret.rs2Length, contextAlice->RNGContext); free(contextAlice->cachedSecret.rs2); contextAlice->cachedSecret.rs2 = NULL; } if (contextAlice->cachedSecret.auxsecret!=NULL) { bzrtp_DestroyKey(contextAlice->cachedSecret.auxsecret, contextAlice->cachedSecret.auxsecretLength, contextAlice->RNGContext); free(contextAlice->cachedSecret.auxsecret); contextAlice->cachedSecret.auxsecret = NULL; } if (contextAlice->cachedSecret.pbxsecret!=NULL) { bzrtp_DestroyKey(contextAlice->cachedSecret.pbxsecret, contextAlice->cachedSecret.pbxsecretLength, contextAlice->RNGContext); free(contextAlice->cachedSecret.pbxsecret); contextAlice->cachedSecret.pbxsecret = NULL; } /*** Do the same for bob ***/ /* get s1 from rs1 or rs2 */ s1=NULL; s2=NULL; s3=NULL; contextBob->channelContext[0]->KDFContextLength = 24+32;/* actual depends on selected hash length*/ contextBob->channelContext[0]->KDFContext = (uint8_t *)malloc(contextBob->channelContext[0]->KDFContextLength*sizeof(uint8_t)); memcpy(contextBob->channelContext[0]->KDFContext, contextBob->peerZID, 12); /* ZIDi*/ memcpy(contextBob->channelContext[0]->KDFContext+12, contextBob->selfZID, 12); /* ZIDr */ memcpy(contextBob->channelContext[0]->KDFContext+24, bob_totalHash, 32); /* total Hash*/ if (contextBob->cachedSecret.rs1 != NULL) { /* if there is a s1 (already validated when received the DHpacket) */ s1 = contextBob->cachedSecret.rs1; s1Length = contextBob->cachedSecret.rs1Length; } else if (contextBob->cachedSecret.rs2 != NULL) { /* otherwise if there is a s2 (already validated when received the DHpacket) */ s1 = contextBob->cachedSecret.rs2; s1Length = contextBob->cachedSecret.rs2Length; } /* s2 is the auxsecret */ s2 = contextBob->cachedSecret.auxsecret; /* this may be null if no match or no aux secret where found */ s2Length = contextBob->cachedSecret.auxsecretLength; /* this may be 0 if no match or no aux secret where found */ /* s3 is the pbxsecret */ s3 = contextBob->cachedSecret.pbxsecret; /* this may be null if no match or no pbx secret where found */ s3Length = contextBob->cachedSecret.pbxsecretLength; /* this may be 0 if no match or no pbx secret where found */ free(dataToHash); dataToHash = (uint8_t *)malloc(totalHashDataLength*sizeof(uint8_t)); dataToHash[0] = 0x00; dataToHash[1] = 0x00; dataToHash[2] = 0x00; dataToHash[3] = 0x01; hashDataIndex = 4; if (contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH2k || contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_DH3k) { bctbx_DHMContext_t *DHMContext = (bctbx_DHMContext_t *)(contextBob->keyAgreementContext); memcpy(dataToHash+hashDataIndex, DHMContext->key, secretLength); } if (contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X255 || contextBob->keyAgreementAlgo == ZRTP_KEYAGREEMENT_X448) { bctbx_ECDHContext_t *ECDHContext = (bctbx_ECDHContext_t *)(contextBob->keyAgreementContext); memcpy(dataToHash+hashDataIndex, ECDHContext->sharedSecret, secretLength); } hashDataIndex += secretLength; memcpy(dataToHash+hashDataIndex, "ZRTP-HMAC-KDF", 13); hashDataIndex += 13; memcpy(dataToHash+hashDataIndex, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength); hashDataIndex += 56; dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s1Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s1Length&0xFF); if (s1!=NULL) { memcpy(dataToHash+hashDataIndex, s1, s1Length); hashDataIndex += s1Length; } dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s2Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s2Length&0xFF); if (s2!=NULL) { memcpy(dataToHash+hashDataIndex, s2, s2Length); hashDataIndex += s2Length; } dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>24)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>16)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)((s3Length>>8)&0xFF); dataToHash[hashDataIndex++] = (uint8_t)(s3Length&0xFF); if (s3!=NULL) { memcpy(dataToHash+hashDataIndex, s3, s3Length); hashDataIndex += s3Length; } contextBob->channelContext[0]->s0 = (uint8_t *)malloc(32*sizeof(uint8_t)); contextBob->channelContext[0]->hashFunction(dataToHash, totalHashDataLength, 32, contextBob->channelContext[0]->s0); free(dataToHash); /* destroy all cached keys in context */ if (contextBob->cachedSecret.rs1!=NULL) { bzrtp_DestroyKey(contextBob->cachedSecret.rs1, contextBob->cachedSecret.rs1Length, contextBob->RNGContext); free(contextBob->cachedSecret.rs1); contextBob->cachedSecret.rs1 = NULL; } if (contextBob->cachedSecret.rs2!=NULL) { bzrtp_DestroyKey(contextBob->cachedSecret.rs2, contextBob->cachedSecret.rs2Length, contextBob->RNGContext); free(contextBob->cachedSecret.rs2); contextBob->cachedSecret.rs2 = NULL; } if (contextBob->cachedSecret.auxsecret!=NULL) { bzrtp_DestroyKey(contextBob->cachedSecret.auxsecret, contextBob->cachedSecret.auxsecretLength, contextBob->RNGContext); free(contextBob->cachedSecret.auxsecret); contextBob->cachedSecret.auxsecret = NULL; } if (contextBob->cachedSecret.pbxsecret!=NULL) { bzrtp_DestroyKey(contextBob->cachedSecret.pbxsecret, contextBob->cachedSecret.pbxsecretLength, contextBob->RNGContext); free(contextBob->cachedSecret.pbxsecret); contextBob->cachedSecret.pbxsecret = NULL; } /* DEBUG compare s0 */ if (memcmp(contextBob->channelContext[0]->s0, contextAlice->channelContext[0]->s0, 32)==0) { bzrtp_message("Got the same s0\n"); BC_PASS("s0 match"); } else { bzrtp_message("ERROR s0 differs\n"); BC_PASS("s0 mismatch"); } /* now compute the ZRTPSession key : section 4.5.2 * ZRTPSess = KDF(s0, "ZRTP Session Key", KDF_Context, negotiated hash length)*/ contextAlice->ZRTPSessLength=32; /* must be set to the length of negotiated hash */ contextAlice->ZRTPSess = (uint8_t *)malloc(contextAlice->ZRTPSessLength*sizeof(uint8_t)); retval = bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"ZRTP Session Key", 16, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, /* this one too depends on selected hash */ contextAlice->channelContext[0]->hashLength, contextAlice->channelContext[0]->hmacFunction, contextAlice->ZRTPSess); contextBob->ZRTPSessLength=32; /* must be set to the length of negotiated hash */ contextBob->ZRTPSess = (uint8_t *)malloc(contextBob->ZRTPSessLength*sizeof(uint8_t)); retval = bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"ZRTP Session Key", 16, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, /* this one too depends on selected hash */ contextBob->channelContext[0]->hashLength, contextBob->channelContext[0]->hmacFunction, contextBob->ZRTPSess); /* DEBUG compare ZRTPSess Key */ if (memcmp(contextBob->ZRTPSess, contextAlice->ZRTPSess, 32)==0) { bzrtp_message("Got the same ZRTPSess\n"); BC_PASS("ZRTPSess match"); } else { bzrtp_message("ERROR ZRTPSess differs\n"); BC_PASS("ZRTPSess mismatch"); } /* compute the sas according to rfc section 4.5.2 sashash = KDF(s0, "SAS", KDF_Context, 256) */ retval = bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"SAS", 3, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, /* this one too depends on selected hash */ 256/8, /* function gets L in bytes */ contextAlice->channelContext[0]->hmacFunction, alice_sasHash); retval = bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"SAS", 3, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, /* this one too depends on selected hash */ 256/8, /* function gets L in bytes */ contextBob->channelContext[0]->hmacFunction, bob_sasHash); /* DEBUG compare sasHash */ if (memcmp(alice_sasHash, bob_sasHash, 32)==0) { bzrtp_message("Got the same SAS Hash\n"); BC_PASS("SAS Hash match"); } else { bzrtp_message("ERROR SAS Hash differs\n"); BC_PASS("SAS Hash mismatch"); } /* display SAS (we shall not do this now but after the confirm message exchanges) */ sasValue = ((uint32_t)alice_sasHash[0]<<24) | ((uint32_t)alice_sasHash[1]<<16) | ((uint32_t)alice_sasHash[2]<<8) | ((uint32_t)(alice_sasHash[3])); contextAlice->channelContext[0]->sasFunction(sasValue, sas, 5); bzrtp_message("Alice SAS is %.4s\n", sas); sasValue = ((uint32_t)bob_sasHash[0]<<24) | ((uint32_t)bob_sasHash[1]<<16) | ((uint32_t)bob_sasHash[2]<<8) | ((uint32_t)(bob_sasHash[3])); contextBob->channelContext[0]->sasFunction(sasValue, sas, 5); bzrtp_message("Bob SAS is %.4s\n", sas); /* now derive the other keys (mackeyi, mackeyr, zrtpkeyi and zrtpkeyr, srtpkeys and salt) */ contextAlice->channelContext[0]->mackeyi = (uint8_t *)malloc(contextAlice->channelContext[0]->hashLength*(sizeof(uint8_t))); contextAlice->channelContext[0]->mackeyr = (uint8_t *)malloc(contextAlice->channelContext[0]->hashLength*(sizeof(uint8_t))); contextAlice->channelContext[0]->zrtpkeyi = (uint8_t *)malloc(contextAlice->channelContext[0]->cipherKeyLength*(sizeof(uint8_t))); contextAlice->channelContext[0]->zrtpkeyr = (uint8_t *)malloc(contextAlice->channelContext[0]->cipherKeyLength*(sizeof(uint8_t))); contextBob->channelContext[0]->mackeyi = (uint8_t *)malloc(contextBob->channelContext[0]->hashLength*(sizeof(uint8_t))); contextBob->channelContext[0]->mackeyr = (uint8_t *)malloc(contextBob->channelContext[0]->hashLength*(sizeof(uint8_t))); contextBob->channelContext[0]->zrtpkeyi = (uint8_t *)malloc(contextBob->channelContext[0]->cipherKeyLength*(sizeof(uint8_t))); contextBob->channelContext[0]->zrtpkeyr = (uint8_t *)malloc(contextBob->channelContext[0]->cipherKeyLength*(sizeof(uint8_t))); /* Alice */ retval = bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"Initiator HMAC key", 18, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, contextAlice->channelContext[0]->hashLength, contextAlice->channelContext[0]->hmacFunction, contextAlice->channelContext[0]->mackeyi); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"Responder HMAC key", 18, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, contextAlice->channelContext[0]->hashLength, contextAlice->channelContext[0]->hmacFunction, contextAlice->channelContext[0]->mackeyr); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"Initiator ZRTP key", 18, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, contextAlice->channelContext[0]->cipherKeyLength, contextAlice->channelContext[0]->hmacFunction, contextAlice->channelContext[0]->zrtpkeyi); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[0]->s0, contextAlice->channelContext[0]->hashLength, (uint8_t *)"Responder ZRTP key", 18, contextAlice->channelContext[0]->KDFContext, contextAlice->channelContext[0]->KDFContextLength, contextAlice->channelContext[0]->cipherKeyLength, contextAlice->channelContext[0]->hmacFunction, contextAlice->channelContext[0]->zrtpkeyr); /* Bob */ retval = bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"Initiator HMAC key", 18, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, contextBob->channelContext[0]->hashLength, contextBob->channelContext[0]->hmacFunction, contextBob->channelContext[0]->mackeyi); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"Responder HMAC key", 18, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, contextBob->channelContext[0]->hashLength, contextBob->channelContext[0]->hmacFunction, contextBob->channelContext[0]->mackeyr); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"Initiator ZRTP key", 18, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, contextBob->channelContext[0]->cipherKeyLength, contextBob->channelContext[0]->hmacFunction, contextBob->channelContext[0]->zrtpkeyi); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[0]->s0, contextBob->channelContext[0]->hashLength, (uint8_t *)"Responder ZRTP key", 18, contextBob->channelContext[0]->KDFContext, contextBob->channelContext[0]->KDFContextLength, contextBob->channelContext[0]->cipherKeyLength, contextBob->channelContext[0]->hmacFunction, contextBob->channelContext[0]->zrtpkeyr); /* DEBUG compare keys */ if ((memcmp(contextAlice->channelContext[0]->mackeyi, contextBob->channelContext[0]->mackeyi, contextAlice->channelContext[0]->hashLength)==0) && (memcmp(contextAlice->channelContext[0]->mackeyr, contextBob->channelContext[0]->mackeyr, contextAlice->channelContext[0]->hashLength)==0) && (memcmp(contextAlice->channelContext[0]->zrtpkeyi, contextBob->channelContext[0]->zrtpkeyi, contextAlice->channelContext[0]->cipherKeyLength)==0) && (memcmp(contextAlice->channelContext[0]->zrtpkeyr, contextBob->channelContext[0]->zrtpkeyr, contextAlice->channelContext[0]->cipherKeyLength)==0)) { bzrtp_message("Got the same keys\n"); BC_PASS("keys match"); } else { bzrtp_message("ERROR keys differ\n"); BC_PASS("Keys mismatch"); } /* now Bob build the CONFIRM1 packet and send it to Alice */ bob_Confirm1 = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[0], MSGTYPE_CONFIRM1, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[0], bob_Confirm1, contextBob->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[0]->selfSequenceNumber++; } bzrtp_message("Bob building Confirm1 return %x\n", retval); alice_Confirm1FromBob = bzrtp_packetCheck(bob_Confirm1->packetString, bob_Confirm1->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_Confirm1->packetString, bob_Confirm1->messageLength+16, alice_Confirm1FromBob); bzrtp_message ("Alice parsing confirm1 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextAlice->channelContext[0]->peerSequenceNumber = alice_Confirm1FromBob->sequenceNumber; alice_Confirm1FromBob_message = (bzrtpConfirmMessage_t *)alice_Confirm1FromBob->messageData; memcpy(contextAlice->channelContext[0]->peerH[0], alice_Confirm1FromBob_message->H0, 32); } packetDump(bob_Confirm1,1); packetDump(alice_Confirm1FromBob,0); bzrtp_freeZrtpPacket(alice_Confirm1FromBob); bzrtp_freeZrtpPacket(bob_Confirm1); /* now Alice build the CONFIRM2 packet and send it to Bob */ alice_Confirm2 = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[0], MSGTYPE_CONFIRM2, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[0], alice_Confirm2, contextAlice->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextAlice->channelContext[0]->selfSequenceNumber++; } bzrtp_message("Alice building Confirm2 return %x\n", retval); bob_Confirm2FromAlice = bzrtp_packetCheck(alice_Confirm2->packetString, alice_Confirm2->messageLength+16, contextBob->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[0], alice_Confirm2->packetString, alice_Confirm2->messageLength+16, bob_Confirm2FromAlice); bzrtp_message ("Bob parsing confirm2 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextBob->channelContext[0]->peerSequenceNumber = bob_Confirm2FromAlice->sequenceNumber; bob_Confirm2FromAlice_message = (bzrtpConfirmMessage_t *)bob_Confirm2FromAlice->messageData; memcpy(contextBob->channelContext[0]->peerH[0], bob_Confirm2FromAlice_message->H0, 32); /* set bob's status to secure */ contextBob->isSecure = 1; } packetDump(alice_Confirm2,1); packetDump(bob_Confirm2FromAlice,0); bzrtp_freeZrtpPacket(bob_Confirm2FromAlice); bzrtp_freeZrtpPacket(alice_Confirm2); /* Bob build the conf2Ack and send it to Alice */ bob_Conf2ACK = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[0], MSGTYPE_CONF2ACK, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[0], bob_Conf2ACK, contextBob->channelContext[0]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[0]->selfSequenceNumber++; } bzrtp_message("Bob building Conf2ACK return %x\n", retval); alice_Conf2ACKFromBob = bzrtp_packetCheck(bob_Conf2ACK->packetString, bob_Conf2ACK->messageLength+16, contextAlice->channelContext[0]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_Conf2ACK->packetString, bob_Conf2ACK->messageLength+16, alice_Conf2ACKFromBob); bzrtp_message ("Alice parsing conf2ACK returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextAlice->channelContext[0]->peerSequenceNumber = alice_Conf2ACKFromBob->sequenceNumber; /* set Alice's status to secure */ contextAlice->isSecure = 1; } bzrtp_freeZrtpPacket(bob_Conf2ACK); bzrtp_freeZrtpPacket(alice_Conf2ACKFromBob); dumpContext("Alice", contextAlice); dumpContext("Bob", contextBob); bzrtp_message("\n\n\n\n\n*************************************************************\n SECOND CHANNEL\n**********************************************\n\n"); /* Now create a second channel for Bob and Alice */ retval = bzrtp_addChannel(contextAlice, 0x45678901); bzrtp_message("Add channel to Alice's context returns %d\n", retval); retval = bzrtp_addChannel(contextBob, 0x54321098); bzrtp_message("Add channel to Bob's context returns %d\n", retval); /* create hello packets for this channel */ alice_Hello = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[1], MSGTYPE_HELLO, &retval); if (bzrtp_packetBuild(contextAlice, contextAlice->channelContext[1], alice_Hello, contextAlice->channelContext[1]->selfSequenceNumber) ==0) { contextAlice->channelContext[1]->selfSequenceNumber++; bzrtp_freeZrtpPacket(contextAlice->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID]); contextAlice->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID] = alice_Hello; } bob_Hello = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[1], MSGTYPE_HELLO, &retval); if (bzrtp_packetBuild(contextBob, contextBob->channelContext[1], bob_Hello, contextBob->channelContext[1]->selfSequenceNumber) ==0) { contextBob->channelContext[1]->selfSequenceNumber++; bzrtp_freeZrtpPacket(contextBob->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID]); contextBob->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID] = bob_Hello; } /* now send Alice Hello's to Bob and vice-versa, so they parse them */ alice_HelloFromBob = bzrtp_packetCheck(bob_Hello->packetString, bob_Hello->messageLength+16, contextAlice->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[0], bob_Hello->packetString, bob_Hello->messageLength+16, alice_HelloFromBob); bzrtp_message ("Alice parsing returns %x\n", retval); if (retval==0) { bzrtpHelloMessage_t *alice_HelloFromBob_message; int i; uint8_t checkPeerSupportMultiChannel = 0; contextAlice->channelContext[1]->peerSequenceNumber = alice_HelloFromBob->sequenceNumber; /* save bob's Hello packet in Alice's context */ contextAlice->channelContext[1]->peerPackets[HELLO_MESSAGE_STORE_ID] = alice_HelloFromBob; /* we are already secured (shall check isSecure==1), so we just need to check that peer Hello have the Mult in his key agreement list of supported algo */ alice_HelloFromBob_message = (bzrtpHelloMessage_t *)alice_HelloFromBob->messageData; for (i=0; ikc; i++) { if (alice_HelloFromBob_message->supportedKeyAgreement[i] == ZRTP_KEYAGREEMENT_Mult) { checkPeerSupportMultiChannel = 1; } } /* ok multi channel is supported*/ if (checkPeerSupportMultiChannel == 1) { bzrtp_message("Alice found that Bob supports multi channel\n"); /* now set the choosen algos, they MUST be the same than main channel (channel 0) except for keyAgreement which is set to mult */ contextAlice->channelContext[1]->hashAlgo = contextAlice->channelContext[0]->hashAlgo; contextAlice->channelContext[1]->hashLength = contextAlice->channelContext[0]->hashLength; contextAlice->channelContext[1]->cipherAlgo = contextAlice->channelContext[0]->cipherAlgo; contextAlice->channelContext[1]->cipherKeyLength = contextAlice->channelContext[0]->cipherKeyLength; contextAlice->channelContext[1]->authTagAlgo = contextAlice->channelContext[0]->authTagAlgo; contextAlice->channelContext[1]->sasAlgo = contextAlice->channelContext[0]->sasAlgo; contextAlice->channelContext[1]->keyAgreementAlgo = ZRTP_KEYAGREEMENT_Mult; contextAlice->channelContext[1]->keyAgreementLength = 0; /* no public values exchanged in Multi channel mode */ bzrtp_updateCryptoFunctionPointers(contextAlice->channelContext[1]); } else { bzrtp_message("ERROR : Alice found that Bob doesn't support multi channel\n"); } } bob_HelloFromAlice = bzrtp_packetCheck(alice_Hello->packetString, alice_Hello->messageLength+16, contextBob->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[1], alice_Hello->packetString, alice_Hello->messageLength+16, bob_HelloFromAlice); bzrtp_message ("Bob parsing returns %x\n", retval); if (retval==0) { bzrtpHelloMessage_t *bob_HelloFromAlice_message; int i; uint8_t checkPeerSupportMultiChannel = 0; contextBob->channelContext[1]->peerSequenceNumber = bob_HelloFromAlice->sequenceNumber; /* save alice's Hello packet in bob's context */ contextBob->channelContext[1]->peerPackets[HELLO_MESSAGE_STORE_ID] = bob_HelloFromAlice; /* we are already secured (shall check isSecure==1), so we just need to check that peer Hello have the Mult in his key agreement list of supported algo */ bob_HelloFromAlice_message = (bzrtpHelloMessage_t *)bob_HelloFromAlice->messageData; for (i=0; ikc; i++) { if (bob_HelloFromAlice_message->supportedKeyAgreement[i] == ZRTP_KEYAGREEMENT_Mult) { checkPeerSupportMultiChannel = 1; } } /* ok multi channel is supported*/ if (checkPeerSupportMultiChannel == 1) { bzrtp_message("Bob found that Alice supports multi channel\n"); /* now set the choosen algos, they MUST be the same than main channel (channel 0) except for keyAgreement which is set to mult */ contextBob->channelContext[1]->hashAlgo = contextBob->channelContext[0]->hashAlgo; contextBob->channelContext[1]->hashLength = contextBob->channelContext[0]->hashLength; contextBob->channelContext[1]->cipherAlgo = contextBob->channelContext[0]->cipherAlgo; contextBob->channelContext[1]->cipherKeyLength = contextBob->channelContext[0]->cipherKeyLength; contextBob->channelContext[1]->authTagAlgo = contextBob->channelContext[0]->authTagAlgo; contextBob->channelContext[1]->sasAlgo = contextBob->channelContext[0]->sasAlgo; contextBob->channelContext[1]->keyAgreementAlgo = ZRTP_KEYAGREEMENT_Mult; contextBob->channelContext[1]->keyAgreementLength = 0; /* no public values exchanged in Multi channel mode */ bzrtp_updateCryptoFunctionPointers(contextBob->channelContext[1]); } else { bzrtp_message("ERROR : Bob found that Alice doesn't support multi channel\n"); } } /* update context with hello message information : H3 and compute initiator and responder's shared secret Hashs */ alice_HelloFromBob_message = (bzrtpHelloMessage_t *)alice_HelloFromBob->messageData; memcpy(contextAlice->channelContext[1]->peerH[3], alice_HelloFromBob_message->H3, 32); bob_HelloFromAlice_message = (bzrtpHelloMessage_t *)bob_HelloFromAlice->messageData; memcpy(contextBob->channelContext[1]->peerH[3], bob_HelloFromAlice_message->H3, 32); /* here we shall exchange Hello ACK but it is just a test and was done already for channel 0, skip it as it is useless for the test */ /* Bob will be the initiator, so compute a commit for him */ bob_Commit = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[1], MSGTYPE_COMMIT, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[1], bob_Commit, contextBob->channelContext[1]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[1]->selfSequenceNumber++; contextBob->channelContext[1]->selfPackets[COMMIT_MESSAGE_STORE_ID] = bob_Commit; } bzrtp_message("Bob building Commit return %x\n", retval); /* and send it to Alice */ alice_CommitFromBob = bzrtp_packetCheck(bob_Commit->packetString, bob_Commit->messageLength+16, contextAlice->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[1], bob_Commit->packetString, bob_Commit->messageLength+16, alice_CommitFromBob); bzrtp_message ("Alice parsing Commit returns %x\n", retval); if (retval==0) { bzrtpCommitMessage_t *alice_CommitFromBob_message; /* update context with the information found in the packet */ contextAlice->channelContext[1]->peerSequenceNumber = alice_CommitFromBob->sequenceNumber; /* Alice will be the initiator (commit contention not implemented in this test) so just discard bob's commit */ alice_CommitFromBob_message = (bzrtpCommitMessage_t *)alice_CommitFromBob->messageData; memcpy(contextAlice->channelContext[1]->peerH[2], alice_CommitFromBob_message->H2, 32); contextAlice->channelContext[1]->peerPackets[COMMIT_MESSAGE_STORE_ID] = alice_CommitFromBob; } packetDump(alice_CommitFromBob, 0); /* for test purpose define Alice as the responder */ contextAlice->channelContext[1]->role = BZRTP_ROLE_RESPONDER; /* compute the total hash as in rfc section 4.4.3.2 total_hash = hash(Hello of responder || Commit) */ totalHashDataLength = alice_Hello->messageLength + bob_Commit->messageLength; dataToHash = (uint8_t *)malloc(totalHashDataLength*sizeof(uint8_t)); hashDataIndex = 0; /* get all data from Alice */ memcpy(dataToHash, contextAlice->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextAlice->channelContext[1]->selfPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextAlice->channelContext[1]->peerPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextAlice->channelContext[1]->peerPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); contextAlice->channelContext[1]->hashFunction(dataToHash, totalHashDataLength, 32, alice_totalHash); /* get all data from Bob */ hashDataIndex = 0; memcpy(dataToHash, contextBob->channelContext[1]->peerPackets[HELLO_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[1]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength); hashDataIndex += contextBob->channelContext[1]->peerPackets[HELLO_MESSAGE_STORE_ID]->messageLength; memcpy(dataToHash+hashDataIndex, contextBob->channelContext[1]->selfPackets[COMMIT_MESSAGE_STORE_ID]->packetString+ZRTP_PACKET_HEADER_LENGTH, contextBob->channelContext[1]->selfPackets[COMMIT_MESSAGE_STORE_ID]->messageLength); contextBob->channelContext[1]->hashFunction(dataToHash, totalHashDataLength, 32, bob_totalHash); if (memcmp(bob_totalHash, alice_totalHash, 32) == 0) { bzrtp_message("Got the same total hash\n"); BC_PASS("Total Hash match"); } else { bzrtp_message("AARGG!! total hash mismatch"); BC_FAIL("Total Hash mismatch"); } free(dataToHash); /* compute the KDF Context as in rfc section 4.4.3.2 KDF_Context = (ZIDi || ZIDr || total_hash) */ contextAlice->channelContext[1]->KDFContextLength = 24 + contextAlice->channelContext[1]->hashLength; contextAlice->channelContext[1]->KDFContext = (uint8_t *)malloc(contextAlice->channelContext[1]->KDFContextLength*sizeof(uint8_t)); memcpy(contextAlice->channelContext[1]->KDFContext, contextAlice->peerZID, 12); memcpy(contextAlice->channelContext[1]->KDFContext+12, contextAlice->selfZID, 12); memcpy(contextAlice->channelContext[1]->KDFContext+24, alice_totalHash, contextAlice->channelContext[1]->hashLength); contextBob->channelContext[1]->KDFContextLength = 24 + contextBob->channelContext[1]->hashLength; contextBob->channelContext[1]->KDFContext = (uint8_t *)malloc(contextBob->channelContext[1]->KDFContextLength*sizeof(uint8_t)); memcpy(contextBob->channelContext[1]->KDFContext, contextBob->selfZID, 12); memcpy(contextBob->channelContext[1]->KDFContext+12, contextBob->peerZID, 12); memcpy(contextBob->channelContext[1]->KDFContext+24, bob_totalHash, contextBob->channelContext[1]->hashLength); if (memcmp(contextBob->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContext, 56) == 0) { bzrtp_message("Got the same total KDF Context\n"); BC_PASS("KDFContext match"); } else { bzrtp_message("AARGG!! KDF Context mismatch"); BC_FAIL("KDF Context mismatch"); } /* compute s0 as in rfc section 4.4.3.2 s0 = KDF(ZRTPSess, "ZRTP MSK", KDF_Context, negotiated hash length) */ contextBob->channelContext[1]->s0 = (uint8_t *)malloc(contextBob->channelContext[1]->hashLength*sizeof(uint8_t)); contextAlice->channelContext[1]->s0 = (uint8_t *)malloc(contextAlice->channelContext[1]->hashLength*sizeof(uint8_t)); retval = bzrtp_keyDerivationFunction(contextBob->ZRTPSess, contextBob->ZRTPSessLength, (uint8_t *)"ZRTP MSK", 8, contextBob->channelContext[1]->KDFContext, contextBob->channelContext[1]->KDFContextLength, /* this one too depends on selected hash */ contextBob->channelContext[1]->hashLength, contextBob->channelContext[1]->hmacFunction, contextBob->channelContext[1]->s0); retval = bzrtp_keyDerivationFunction(contextAlice->ZRTPSess, contextAlice->ZRTPSessLength, (uint8_t *)"ZRTP MSK", 8, contextAlice->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContextLength, /* this one too depends on selected hash */ contextAlice->channelContext[1]->hashLength, contextAlice->channelContext[1]->hmacFunction, contextAlice->channelContext[1]->s0); if (memcmp(contextBob->channelContext[1]->s0, contextAlice->channelContext[1]->s0, contextAlice->channelContext[1]->hashLength) == 0) { bzrtp_message("Got the same s0\n"); BC_PASS("s0 match"); } else { bzrtp_message("AARGG!! s0 mismatch"); BC_FAIL("s0 mismatch"); } /* the rest of key derivation is common to DH mode, no need to test it as it has been done before for channel 0 */ /* we must anyway derive zrtp and mac key for initiator and responder in order to be able to build the confirm packets */ contextAlice->channelContext[1]->mackeyi = (uint8_t *)malloc(contextAlice->channelContext[1]->hashLength*(sizeof(uint8_t))); contextAlice->channelContext[1]->mackeyr = (uint8_t *)malloc(contextAlice->channelContext[1]->hashLength*(sizeof(uint8_t))); contextAlice->channelContext[1]->zrtpkeyi = (uint8_t *)malloc(contextAlice->channelContext[1]->cipherKeyLength*(sizeof(uint8_t))); contextAlice->channelContext[1]->zrtpkeyr = (uint8_t *)malloc(contextAlice->channelContext[1]->cipherKeyLength*(sizeof(uint8_t))); contextBob->channelContext[1]->mackeyi = (uint8_t *)malloc(contextBob->channelContext[1]->hashLength*(sizeof(uint8_t))); contextBob->channelContext[1]->mackeyr = (uint8_t *)malloc(contextBob->channelContext[1]->hashLength*(sizeof(uint8_t))); contextBob->channelContext[1]->zrtpkeyi = (uint8_t *)malloc(contextBob->channelContext[1]->cipherKeyLength*(sizeof(uint8_t))); contextBob->channelContext[1]->zrtpkeyr = (uint8_t *)malloc(contextBob->channelContext[1]->cipherKeyLength*(sizeof(uint8_t))); /* Alice */ retval = bzrtp_keyDerivationFunction(contextAlice->channelContext[1]->s0, contextAlice->channelContext[1]->hashLength, (uint8_t *)"Initiator HMAC key", 18, contextAlice->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContextLength, contextAlice->channelContext[1]->hashLength, contextAlice->channelContext[1]->hmacFunction, contextAlice->channelContext[1]->mackeyi); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[1]->s0, contextAlice->channelContext[1]->hashLength, (uint8_t *)"Responder HMAC key", 18, contextAlice->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContextLength, contextAlice->channelContext[1]->hashLength, contextAlice->channelContext[1]->hmacFunction, contextAlice->channelContext[1]->mackeyr); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[1]->s0, contextAlice->channelContext[1]->hashLength, (uint8_t *)"Initiator ZRTP key", 18, contextAlice->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContextLength, contextAlice->channelContext[1]->cipherKeyLength, contextAlice->channelContext[1]->hmacFunction, contextAlice->channelContext[1]->zrtpkeyi); retval += bzrtp_keyDerivationFunction(contextAlice->channelContext[1]->s0, contextAlice->channelContext[1]->hashLength, (uint8_t *)"Responder ZRTP key", 18, contextAlice->channelContext[1]->KDFContext, contextAlice->channelContext[1]->KDFContextLength, contextAlice->channelContext[1]->cipherKeyLength, contextAlice->channelContext[1]->hmacFunction, contextAlice->channelContext[1]->zrtpkeyr); /* Bob */ retval = bzrtp_keyDerivationFunction(contextBob->channelContext[1]->s0, contextBob->channelContext[1]->hashLength, (uint8_t *)"Initiator HMAC key", 18, contextBob->channelContext[1]->KDFContext, contextBob->channelContext[1]->KDFContextLength, contextBob->channelContext[1]->hashLength, contextBob->channelContext[1]->hmacFunction, contextBob->channelContext[1]->mackeyi); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[1]->s0, contextBob->channelContext[1]->hashLength, (uint8_t *)"Responder HMAC key", 18, contextBob->channelContext[1]->KDFContext, contextBob->channelContext[1]->KDFContextLength, contextBob->channelContext[1]->hashLength, contextBob->channelContext[1]->hmacFunction, contextBob->channelContext[1]->mackeyr); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[1]->s0, contextBob->channelContext[1]->hashLength, (uint8_t *)"Initiator ZRTP key", 18, contextBob->channelContext[1]->KDFContext, contextBob->channelContext[1]->KDFContextLength, contextBob->channelContext[1]->cipherKeyLength, contextBob->channelContext[1]->hmacFunction, contextBob->channelContext[1]->zrtpkeyi); retval += bzrtp_keyDerivationFunction(contextBob->channelContext[1]->s0, contextBob->channelContext[1]->hashLength, (uint8_t *)"Responder ZRTP key", 18, contextBob->channelContext[1]->KDFContext, contextBob->channelContext[1]->KDFContextLength, contextBob->channelContext[1]->cipherKeyLength, contextBob->channelContext[1]->hmacFunction, contextBob->channelContext[1]->zrtpkeyr); /* DEBUG compare keys */ if ((memcmp(contextAlice->channelContext[1]->mackeyi, contextBob->channelContext[1]->mackeyi, contextAlice->channelContext[1]->hashLength)==0) && (memcmp(contextAlice->channelContext[1]->mackeyr, contextBob->channelContext[1]->mackeyr, contextAlice->channelContext[1]->hashLength)==0) && (memcmp(contextAlice->channelContext[1]->zrtpkeyi, contextBob->channelContext[1]->zrtpkeyi, contextAlice->channelContext[1]->cipherKeyLength)==0) && (memcmp(contextAlice->channelContext[1]->zrtpkeyr, contextBob->channelContext[1]->zrtpkeyr, contextAlice->channelContext[1]->cipherKeyLength)==0)) { bzrtp_message("Got the same keys\n"); BC_PASS("keys match"); } else { bzrtp_message("ERROR keys differ\n"); BC_PASS("Keys mismatch"); } /* now Alice build a confirm1 packet */ alice_Confirm1 = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[1], MSGTYPE_CONFIRM1, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[1], alice_Confirm1, contextAlice->channelContext[1]->selfSequenceNumber); if (retval == 0) { contextAlice->channelContext[1]->selfSequenceNumber++; } bzrtp_message("Alice building Confirm1 return %x\n", retval); bob_Confirm1FromAlice = bzrtp_packetCheck(alice_Confirm1->packetString, alice_Confirm1->messageLength+16, contextBob->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[1], alice_Confirm1->packetString, alice_Confirm1->messageLength+16, bob_Confirm1FromAlice); bzrtp_message ("Bob parsing confirm1 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextBob->channelContext[1]->peerSequenceNumber = bob_Confirm1FromAlice->sequenceNumber; bob_Confirm1FromAlice_message = (bzrtpConfirmMessage_t *)bob_Confirm1FromAlice->messageData; memcpy(contextBob->channelContext[1]->peerH[0], bob_Confirm1FromAlice_message->H0, 32); } packetDump(bob_Confirm1FromAlice,0); bzrtp_freeZrtpPacket(bob_Confirm1FromAlice); bzrtp_freeZrtpPacket(alice_Confirm1); /* now Bob build the CONFIRM2 packet and send it to Alice */ bob_Confirm2 = bzrtp_createZrtpPacket(contextBob, contextBob->channelContext[1], MSGTYPE_CONFIRM2, &retval); retval += bzrtp_packetBuild(contextBob, contextBob->channelContext[1], bob_Confirm2, contextBob->channelContext[1]->selfSequenceNumber); if (retval == 0) { contextBob->channelContext[1]->selfSequenceNumber++; } bzrtp_message("Bob building Confirm2 return %x\n", retval); alice_Confirm2FromBob = bzrtp_packetCheck(bob_Confirm2->packetString, bob_Confirm2->messageLength+16, contextAlice->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextAlice, contextAlice->channelContext[1], bob_Confirm2->packetString, bob_Confirm2->messageLength+16, alice_Confirm2FromBob); bzrtp_message ("Alice parsing confirm2 returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextAlice->channelContext[1]->peerSequenceNumber = alice_Confirm2FromBob->sequenceNumber; alice_Confirm2FromBob_message = (bzrtpConfirmMessage_t *)alice_Confirm2FromBob->messageData; memcpy(contextAlice->channelContext[1]->peerH[0], alice_Confirm2FromBob_message->H0, 32); } packetDump(alice_Confirm2FromBob,0); bzrtp_freeZrtpPacket(alice_Confirm2FromBob); bzrtp_freeZrtpPacket(bob_Confirm2); /* Alice build the conf2Ack and send it to Bob */ alice_Conf2ACK = bzrtp_createZrtpPacket(contextAlice, contextAlice->channelContext[1], MSGTYPE_CONF2ACK, &retval); retval += bzrtp_packetBuild(contextAlice, contextAlice->channelContext[1], alice_Conf2ACK, contextAlice->channelContext[1]->selfSequenceNumber); if (retval == 0) { contextAlice->channelContext[1]->selfSequenceNumber++; } bzrtp_message("Alice building Conf2ACK return %x\n", retval); bob_Conf2ACKFromAlice = bzrtp_packetCheck(alice_Conf2ACK->packetString, alice_Conf2ACK->messageLength+16, contextBob->channelContext[1]->peerSequenceNumber, &retval); retval += bzrtp_packetParser(contextBob, contextBob->channelContext[1], alice_Conf2ACK->packetString, alice_Conf2ACK->messageLength+16, bob_Conf2ACKFromAlice); bzrtp_message ("Bob parsing conf2ACK returns %x\n", retval); if (retval==0) { /* update context with the information found in the packet */ contextBob->channelContext[1]->peerSequenceNumber = bob_Conf2ACKFromAlice->sequenceNumber; } bzrtp_freeZrtpPacket(alice_Conf2ACK); bzrtp_freeZrtpPacket(bob_Conf2ACKFromAlice); /* dumpContext("\nAlice", contextAlice); dumpContext("\nBob", contextBob); */ bzrtp_message("Destroy the contexts\n"); /* destroy the context */ bzrtp_destroyBzrtpContext(contextAlice, 0x45678901); bzrtp_destroyBzrtpContext(contextBob, 0x54321098); bzrtp_message("Destroy the contexts last channel\n"); bzrtp_destroyBzrtpContext(contextBob, 0x87654321); bzrtp_destroyBzrtpContext(contextAlice, 0x12345678); } typedef struct packetDatas_struct { uint8_t packetString[1000]; uint16_t packetLength; } packetDatas_t; /* Alice and Bob packet queues are globals */ packetDatas_t aliceQueue[10]; packetDatas_t bobQueue[10]; uint8_t aliceQueueIndex = 0; uint8_t bobQueueIndex = 0; uint8_t block_Hello = 0; /* this is a callback function for send data, just dump the packet */ /* client Data is a my_Context_t structure */ int bzrtp_sendData(void *clientData, const uint8_t *packetString, uint16_t packetLength) { /* get the client Data */ my_Context_t *contexts = (my_Context_t *)clientData; /* bzrtp_message ("%s sends a message!\n", contexts->nom); int retval; bzrtpPacket_t *zrtpPacket = bzrtp_packetCheck(packetString, packetLength, contexts->peerChannelContext->peerSequenceNumber, &retval); if (retval==0) { retval = bzrtp_packetParser(contexts->peerContext, contexts->peerChannelContext, packetString, packetLength, zrtpPacket); if (retval == 0) { */ /* packetDump(zrtpPacket,0); */ /* printHex("Data", packetString, packetLength);*/ /* } else { bzrtp_message("Parse says %04x\n", retval); } } else { bzrtp_message("Check says %04x\n", retval); } */ /* put the message in the message queue */ if (contexts->nom[0] == 'A') { /* message sent by Alice, put it in Bob's queue */ /* block the first Hello to force going through wait for hello state and check it is retransmitted */ /* if ((block_Hello == 0) && (zrtpPacket->messageType == MSGTYPE_HELLO)) { block_Hello = 1; } else {*/ memcpy(bobQueue[bobQueueIndex].packetString, packetString, packetLength); bobQueue[bobQueueIndex++].packetLength = packetLength; /* }*/ } else { memcpy(aliceQueue[aliceQueueIndex].packetString, packetString, packetLength); aliceQueue[aliceQueueIndex++].packetLength = packetLength; } /* bzrtp_freeZrtpPacket(zrtpPacket); */ return 0; } uint64_t myCurrentTime = 0; /* we do not need a real time, start at 0 and increment it at each sleep */ uint64_t getCurrentTimeInMs() { return myCurrentTime; } static void sleepMs(int ms){ #ifdef _WIN32 Sleep(ms); #else struct timespec ts; ts.tv_sec=0; ts.tv_nsec=ms*1000000LL; nanosleep(&ts,NULL); #endif myCurrentTime +=ms; } /* Ping message length is 24 bytes (already define in packetParser.c out of this scope) */ #define ZRTP_PINGMESSAGE_FIXED_LENGTH 24 static void test_stateMachine() { int retval; my_Context_t aliceClientData, bobClientData; uint64_t initialTime; uint8_t pingPacketString[ZRTP_PACKET_OVERHEAD+ZRTP_PINGMESSAGE_FIXED_LENGTH]; /* there is no builder for ping packet and it is 24 bytes long(12 bytes of message header, 12 of data + packet overhead*/ uint32_t CRC; uint8_t *CRCbuffer; my_Context_t aliceSecondChannelClientData, bobSecondChannelClientData; bzrtpCallbacks_t cbs={0} ; /* Create zrtp Context */ bzrtpContext_t *contextAlice = bzrtp_createBzrtpContext(); bzrtpContext_t *contextBob = bzrtp_createBzrtpContext(); /* set the cache related callback functions */ cbs.bzrtp_sendData=bzrtp_sendData; bzrtp_setCallbacks(contextAlice, &cbs); cbs.bzrtp_sendData=bzrtp_sendData; bzrtp_setCallbacks(contextBob, &cbs); /* create the client Data and associate them to the channel contexts */ memcpy(aliceClientData.nom, "Alice", 6); memcpy(bobClientData.nom, "Bob", 4); aliceClientData.peerContext = contextBob; aliceClientData.peerChannelContext = contextBob->channelContext[0]; bobClientData.peerContext = contextAlice; bobClientData.peerChannelContext = contextAlice->channelContext[0]; /* run the init */ bzrtp_initBzrtpContext(contextAlice, 0x12345678);/* Alice's SSRC of main channel is 12345678 */ bzrtp_initBzrtpContext(contextBob, 0x87654321); /* Bob's SSRC of main channel is 87654321 */ retval = bzrtp_setClientData(contextAlice, 0x12345678, (void *)&aliceClientData); retval += bzrtp_setClientData(contextBob, 0x87654321, (void *)&bobClientData); bzrtp_message("Set client data return %x\n", retval); /* now start the engine */ initialTime = getCurrentTimeInMs(); retval = bzrtp_startChannelEngine(contextAlice, 0x12345678); bzrtp_message ("Alice starts return %x\n", retval); retval = bzrtp_startChannelEngine(contextBob, 0x87654321); bzrtp_message ("Bob starts return %x\n", retval); /* now start infinite loop until we reach secure state */ while ((contextAlice->isSecure == 0 || contextBob->isSecure == 0) && (getCurrentTimeInMs()-initialTime<5000)){ int i; /* first check the message queue */ for (i=0; iisSecure == 1) && (contextBob->isSecure == 1)) { /* don't compare sas if we're not secure at we may not have it */ BC_ASSERT_TRUE((memcmp(contextAlice->channelContext[0]->srtpSecrets.sas, contextBob->channelContext[0]->srtpSecrets.sas, 4) == 0)); /* call the set verified Sas function */ bzrtp_SASVerified(contextAlice); bzrtp_SASVerified(contextBob); } else { BC_FAIL("Unable to reach secure state"); } /*** Send alice a ping message from Bob ***/ /* set packet header and CRC */ /* preambule */ pingPacketString[0] = 0x10; pingPacketString[1] = 0x00; /* Sequence number */ pingPacketString[2] = (uint8_t)((contextBob->channelContext[0]->selfSequenceNumber>>8)&0x00FF); pingPacketString[3] = (uint8_t)(contextBob->channelContext[0]->selfSequenceNumber&0x00FF); /* ZRTP magic cookie */ pingPacketString[4] = (uint8_t)((ZRTP_MAGIC_COOKIE>>24)&0xFF); pingPacketString[5] = (uint8_t)((ZRTP_MAGIC_COOKIE>>16)&0xFF); pingPacketString[6] = (uint8_t)((ZRTP_MAGIC_COOKIE>>8)&0xFF); pingPacketString[7] = (uint8_t)(ZRTP_MAGIC_COOKIE&0xFF); /* Source Identifier : insert bob's one: 0x87654321 */ pingPacketString[8] = 0x87; pingPacketString[9] = 0x65; pingPacketString[10] = 0x43; pingPacketString[11] = 0x21; /* message header */ pingPacketString[12] = 0x50; pingPacketString[13] = 0x5a; /* length in 32 bits words */ pingPacketString[14] = 0x00; pingPacketString[15] = 0x06; /* message type "Ping " */ memcpy(pingPacketString+16, "Ping ",8); /* Version on 4 bytes is "1.10" */ memcpy(pingPacketString+24, "1.10", 4); /* a endPointHash, use the first 8 bytes of Bob's ZID */ memcpy(pingPacketString+28, contextBob->selfZID, 8); /* CRC */ CRC = bzrtp_CRC32(pingPacketString, ZRTP_PINGMESSAGE_FIXED_LENGTH+ZRTP_PACKET_HEADER_LENGTH); CRCbuffer = pingPacketString+ZRTP_PINGMESSAGE_FIXED_LENGTH+ZRTP_PACKET_HEADER_LENGTH; *CRCbuffer = (uint8_t)((CRC>>24)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>16)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)((CRC>>8)&0xFF); CRCbuffer++; *CRCbuffer = (uint8_t)(CRC&0xFF); bzrtp_message("Process a PING message for Alice\n"); retval = bzrtp_processMessage(contextAlice, 0x12345678, pingPacketString, ZRTP_PACKET_OVERHEAD+ZRTP_PINGMESSAGE_FIXED_LENGTH); bzrtp_message("Alice processed PING message and return %04x\n\n", retval); /*** now add a second channel ***/ retval = bzrtp_addChannel(contextAlice, 0x34567890); bzrtp_message("Add a channel to Alice context, return %x\n", retval); retval = bzrtp_addChannel(contextBob, 0x09876543); bzrtp_message("Add a channel to Bob context, return %x\n", retval); /* create the client Data and associate them to the channel contexts */ memcpy(aliceSecondChannelClientData.nom, "Alice", 6); memcpy(bobSecondChannelClientData.nom, "Bob", 4); aliceSecondChannelClientData.peerContext = contextBob; aliceSecondChannelClientData.peerChannelContext = contextBob->channelContext[1]; bobSecondChannelClientData.peerContext = contextAlice; bobSecondChannelClientData.peerChannelContext = contextAlice->channelContext[1]; retval = bzrtp_setClientData(contextAlice, 0x34567890, (void *)&aliceSecondChannelClientData); retval += bzrtp_setClientData(contextBob, 0x09876543, (void *)&bobSecondChannelClientData); bzrtp_message("Set client data return %x\n", retval); /* start the channels */ retval = bzrtp_startChannelEngine(contextAlice, 0x34567890); bzrtp_message ("Alice starts return %x\n", retval); retval = bzrtp_startChannelEngine(contextBob, 0x09876543); bzrtp_message ("Bob starts return %x\n", retval); /* now start infinite loop until we reach secure state */ while ((getCurrentTimeInMs()-initialTime<2000)){ int i; /* first check the message queue */ for (i=0; ichannelContext[1]->srtpSecrets.selfSrtpKey, contextBob->channelContext[1]->srtpSecrets.peerSrtpKey, 16) == 0) && (contextAlice->isSecure == 1) && (contextBob->isSecure == 1)); dumpContext("\nAlice", contextAlice); dumpContext("\nBob", contextBob); bzrtp_message("Destroy the contexts\n"); /* destroy the context */ bzrtp_destroyBzrtpContext(contextAlice, 0x34567890); bzrtp_destroyBzrtpContext(contextBob, 0x09876543); bzrtp_message("Destroy the contexts last channel\n"); bzrtp_destroyBzrtpContext(contextBob, 0x87654321); bzrtp_destroyBzrtpContext(contextAlice, 0x12345678); } /* first parse a packet and then try good and bad zrtp-hash, then do it the other way : set the zrtp-hash and then parse packet */ static void test_zrtphash(void) { bzrtpPacket_t *zrtpPacket; int retval; /* Create zrtp Context to use H0-H3 chains and others */ bzrtpContext_t *context12345678 = bzrtp_createBzrtpContext(); /* init the context so it's ready to receive a packet */ bzrtp_initBzrtpContext(context12345678, 0x12345678); /* parse the hello packet */ zrtpPacket = bzrtp_packetCheck(HelloPacketZrtpHash, sizeof(HelloPacketZrtpHash), 0, &retval); BC_ASSERT_EQUAL(retval, 0, int, "%d"); retval = bzrtp_packetParser(context12345678, context12345678->channelContext[0], HelloPacketZrtpHash, sizeof(HelloPacketZrtpHash), zrtpPacket); BC_ASSERT_EQUAL(retval, 0, int, "%d"); /* store it in the peer hello hash structure so we can check it against the zrtp-hash */ context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID] = zrtpPacket; retval = bzrtp_setPeerHelloHash(context12345678, 0x12345678, (uint8_t *)ZRTPHASHPATTERN, strlen((const char *)ZRTPHASHPATTERN)); BC_ASSERT_EQUAL(retval, 0, int, "%d"); /* set a wrong hello hash, this will also reset the session */ retval = bzrtp_setPeerHelloHash(context12345678, 0x12345678, (uint8_t *)ZRTPHASHPATTERN_WRONG, strlen((const char *)ZRTPHASHPATTERN)); BC_ASSERT_EQUAL(retval, BZRTP_ERROR_HELLOHASH_MISMATCH, int, "%d"); BC_ASSERT_TRUE(context12345678->channelContext[0]->peerPackets[HELLO_MESSAGE_STORE_ID]==NULL); /* session must have been reset, peer packets are cancelled */ /* Parse again the hello packet, the peer hello hash is still in the context and incorrect, so good packet must be rejected */ zrtpPacket = bzrtp_packetCheck(HelloPacketZrtpHash, sizeof(HelloPacketZrtpHash), 0, &retval); BC_ASSERT_EQUAL(retval, 0, int, "%d"); retval = bzrtp_packetParser(context12345678, context12345678->channelContext[0], HelloPacketZrtpHash, sizeof(HelloPacketZrtpHash), zrtpPacket); BC_ASSERT_EQUAL(retval, BZRTP_ERROR_HELLOHASH_MISMATCH, int, "%d"); /* set the correct peer hello hash(no packet in context so it just store it) and try again to parse the correct packet */ retval = bzrtp_setPeerHelloHash(context12345678, 0x12345678, (uint8_t *)ZRTPHASHPATTERN, strlen((const char *)ZRTPHASHPATTERN)); BC_ASSERT_EQUAL(retval, 0, int, "%d"); retval = bzrtp_packetParser(context12345678, context12345678->channelContext[0], HelloPacketZrtpHash, sizeof(HelloPacketZrtpHash), zrtpPacket); BC_ASSERT_EQUAL(retval, 0, int, "%d"); /* cleaning */ bzrtp_destroyBzrtpContext(context12345678, 0x12345678); bzrtp_freeZrtpPacket(zrtpPacket); } static test_t packet_parser_tests[] = { TEST_NO_TAG("Parse", test_parser), TEST_NO_TAG("Parse hvi check fail", test_parser_hvi), TEST_NO_TAG("Parse Exchange", test_parserComplete), TEST_NO_TAG("State machine", test_stateMachine), TEST_NO_TAG("ZRTP-hash", test_zrtphash) }; test_suite_t packet_parser_test_suite = { "Packet Parser", NULL, NULL, NULL, NULL, sizeof(packet_parser_tests) / sizeof(packet_parser_tests[0]), packet_parser_tests }; bzrtp-4.4.13/test/bzrtpTest.c000066400000000000000000000072631364144501400161020ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "bzrtpTest.h" #include "typedef.h" #include "testUtils.h" static const char *log_domain = "bzrtp-tester"; static void log_handler(int lev, const char *fmt, va_list args) { #ifdef _WIN32 /* We must use stdio to avoid log formatting (for autocompletion etc.) */ vfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, args); fprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, "\n"); #else va_list cap; va_copy(cap,args); vfprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, fmt, cap); fprintf(lev == BCTBX_LOG_ERROR ? stderr : stdout, "\n"); va_end(cap); #endif bctbx_logv(log_domain, (BctbxLogLevel)lev, fmt, args); } int bzrtp_tester_set_log_file(const char *filename) { int res = 0; char *dir = bctbx_dirname(filename); char *base = bctbx_basename(filename); bctbx_message("Redirecting traces to file [%s]", filename); bctbx_log_handler_t *filehandler = bctbx_create_file_log_handler(0, dir, base); if (filehandler == NULL) { res = -1; goto end; } bctbx_add_log_handler(filehandler); end: if (dir) bctbx_free(dir); if (base) bctbx_free(base); return res; } int silent_arg_func(const char *arg) { bctbx_set_log_level(log_domain, BCTBX_LOG_FATAL); bctbx_set_log_level(BCTBX_LOG_DOMAIN, BCTBX_LOG_FATAL); verbose = 0; return 0; } int verbose_arg_func(const char *arg) { bctbx_set_log_level(log_domain, BCTBX_LOG_DEBUG); bctbx_set_log_level(BCTBX_LOG_DOMAIN,BCTBX_LOG_DEBUG); verbose = 1; return 0; } int logfile_arg_func(const char *arg) { if (bzrtp_tester_set_log_file(arg) < 0) return -2; return 0; } void bzrtp_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args)) { bc_tester_set_silent_func(silent_arg_func); bc_tester_set_verbose_func(verbose_arg_func); bc_tester_set_logfile_func(logfile_arg_func); if (ftester_printf == NULL) ftester_printf = log_handler; bc_tester_init(ftester_printf, BCTBX_LOG_MESSAGE, BCTBX_LOG_ERROR, NULL); bc_tester_add_suite(&crypto_utils_test_suite); bc_tester_add_suite(&packet_parser_test_suite); bc_tester_add_suite(&key_exchange_test_suite); bc_tester_add_suite(&zidcache_test_suite); } void bzrtp_tester_uninit(void) { bc_tester_uninit(); } #if !defined(__ANDROID__) && !(defined(BCTBX_WINDOWS_PHONE) || defined(BCTBX_WINDOWS_UNIVERSAL)) static const char* lime_helper = ""; int main(int argc, char *argv[]) { int i; int ret; bzrtp_tester_init(NULL); if (strstr(argv[0], ".libs")) { int prefix_length = (int)(strstr(argv[0], ".libs") - argv[0]) + 1; char prefix[200]; sprintf(prefix, "%s%.*s", argv[0][0] == '/' ? "" : "./", prefix_length, argv[0]); bc_tester_set_resource_dir_prefix(prefix); bc_tester_set_writable_dir_prefix(prefix); } for(i = 1; i < argc; ++i) { int ret = bc_tester_parse_args(argc, argv, i); if (ret>0) { i += ret - 1; continue; } else if (ret<0) { bc_tester_helper(argv[0], lime_helper); } return ret; } ret = bc_tester_start(argv[0]); bzrtp_tester_uninit(); bctbx_uninit_logger(); return ret; } #endif bzrtp-4.4.13/test/bzrtpTest.h000066400000000000000000000022101364144501400160720ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef bzrtpTester_h #define bzrtpTester_h #define BCTOOLBOX_LOG_DOMAIN "bzrtp-tester" #include #define bzrtp_message bctbx_message #include extern test_suite_t crypto_utils_test_suite; extern test_suite_t packet_parser_test_suite; extern test_suite_t zidcache_test_suite; extern test_suite_t key_exchange_test_suite; extern int verbose; #endif // bzrtpTester_h bzrtp-4.4.13/test/bzrtpZidCacheTest.c000066400000000000000000000360711364144501400174740ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "bzrtp/bzrtp.h" #include "testUtils.h" #include "zidCache.h" #include "bzrtpTest.h" #ifdef ZIDCACHE_ENABLED #include "sqlite3.h" #ifdef HAVE_LIBXML2 #include #include static const char *xmlCacheMigration = "\n00112233445566778899aabb99887766554433221100ffeec4274f13a2b6fa05c15ec93158f930e7264b0a893393376dbc80c6eb1cccdc5asip:bob@sip.linphone.org219d9e445d10d4ed64083c7ccbb83a23bc17a97df0af5de4261f3fe026b05b0b747e72a5cc996413cb9fa6e3d18d8b370436e274cd6ba4efc1a4580340af57cadf2bf38e719fa89e17332cf8d5e774ee70d347baa74d16dee01f306c54789869928ce78b0bfc30427a02b1b668b2b3b0496d5664d7e89b75ed292ee97e3fc850496bcc8959337abe5dda11f388384b349d210612f30824268a3753a7afa52ef6df5866dca76315c4sip:bob2@sip.linphone.orgffeeddccbbaa987654321012858b495dfad483af3c088f26d68c4beebc638bd44feae45aea726a771727235esip:bob@sip.linphone.orgb6aac945057bc4466bfe9a23771c6a1b3b8d72ec3e7d8f30ed63cbc5a9479a25bea5ac3225edd0545b816f061a8190370e3ee5160e75404846a34d1580e0c26317ce70fdf12e500294bcb5f2ffef53096761bb1c912b21e972ae03a5a9f05c477e13a20e15a517700f0be0921f74b96d4b4a0c539d5e14d5cdd8706441874ac075e18caa2cfbbf061533dee20c8116dc2c282cae9adfea689b87bc4c6a4e18a846f12e3e7fea39590987654321fedcba5a5a5a5acb6ecc87d1dd87b23f225eec53a26fc541384917623e0c46abab8c0350c6929e92bb03988e8f0ccfefa37a55fd7c5893bea3bfbb27312f49dd9b10d0e3c15fc72315705a5830b98f68458fcd49623144cb34a667512c4d44686aee125bb8b62294c56eea0dd829379263b6da3f6ac0a95388090f168a3568736ca0bd9f8d595fc319ae0d41183fec90afc412d42253c5b456580f7a463c111c7293623b8631f4sip:bob@sip.linphone.org2c46ddcc15f5779e0000000058f095bf01"; #endif /* HAVE_LIBXML2 */ #endif /* ZIDCACHE_ENABLED */ static void test_cache_getSelfZID(void) { #ifdef ZIDCACHE_ENABLED bzrtpContext_t *aliceContext; sqlite3 *aliceDB=NULL; uint8_t selfZIDalice[12]; uint8_t selfZIDecila[12]; char patternFilename[1024]; uint8_t patternZIDalice[12] = {0x42, 0x31, 0xf2, 0x9a, 0x6b, 0x14, 0xbc, 0xdf, 0x69, 0x72, 0x9f, 0xb8}; uint8_t patternZIDecila[12] = {0x17, 0x72, 0xae, 0x6b, 0x57, 0xb0, 0x3d, 0x99, 0x96, 0x3d, 0x63, 0xb2}; uint8_t patternZIDbob[12] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xed, 0xcb, 0xa9, 0x87}; char *resource_dir = (char *)bc_tester_get_resource_dir_prefix(); int ret,zuidalicebob=0, zuidCheck=0; /* write/read patterns: write rs1, rs2 and pvs, read also aux*/ int patternLength = 3; uint8_t rs1Value[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}; uint8_t rs2Value[] = {0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0xff, 0xee}; uint8_t pvsValue[] = {0x1}; const char *patternColNames[]={"rs1", "rs2", "pvs", "aux"}; uint8_t *patternColValues[] = {rs1Value, rs2Value, pvsValue}; size_t patternColValuesLength[] = {16, 16, 1}; uint8_t *readValues[] = {NULL, NULL, NULL,NULL}; size_t readLength[4]; int i; char *aliceCacheFile = bc_tester_file("tmpZIDAlice.sqlite");; /* we need a bzrtp context */ aliceContext = bzrtp_createBzrtpContext(); /* create a new DB file */ remove(aliceCacheFile); bzrtptester_sqlite3_open(aliceCacheFile, &aliceDB); /* check what happend if we try to run cacheless */ BC_ASSERT_EQUAL(bzrtp_setZIDCache(aliceContext, NULL, "alice@sip.linphone.org", "bob@sip.linphone.org"),BZRTP_ZIDCACHE_RUNTIME_CACHELESS, int, "%x"); /* add real cache data into context using the dedicated function */ BC_ASSERT_EQUAL(bzrtp_setZIDCache(aliceContext, (void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org"), BZRTP_CACHE_SETUP,int,"%x"); sqlite3_close(aliceDB); /* close the DB and reopen/init it, check the return value is now 0*/ bzrtptester_sqlite3_open(aliceCacheFile, &aliceDB); BC_ASSERT_EQUAL(bzrtp_setZIDCache(aliceContext, (void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org"), 0,int,"%x"); /* call to get selfZID, it will create the ZID for alice@sip.linphone.org. */ /* Note: in regular use we must call bzrtp_initBzrtpContext but here we are just testing the getSelfZID */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceContext->zidCache, aliceContext->selfURI, aliceContext->selfZID, aliceContext->RNGContext, NULL), 0, int, "%x"); /* get it again, in another buffer and compare */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "alice@sip.linphone.org", selfZIDalice, aliceContext->RNGContext, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDalice, aliceContext->selfZID, 12), 0, int, "%d"); /* fetch a ZID for an other adress on the same cache, it shall create a new one */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "ecila@sip.linphone.org", aliceContext->selfZID, aliceContext->RNGContext, NULL), 0, int, "%x"); BC_ASSERT_NOT_EQUAL(memcmp(selfZIDalice, aliceContext->selfZID,12), 0, int, "%d"); /* check it is a different one */ /* fetch it again and compare */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "ecila@sip.linphone.org", selfZIDecila, aliceContext->RNGContext, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDecila, aliceContext->selfZID, 12), 0, int, "%d"); /* fetch again the first one and compare */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "alice@sip.linphone.org", aliceContext->selfZID, aliceContext->RNGContext, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDalice, aliceContext->selfZID, 12), 0, int, "%d"); /* try to get a zuid on alice/bob+patternZIDbob, at first the row shall be created */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_INSERT_ZUID, &zuidalicebob, NULL), 0, int, "%x"); /* ask for it again and check they are the same */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidCheck, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(zuidalicebob, zuidCheck, int, "%d"); /* Then write in cache zrtp table */ BC_ASSERT_EQUAL(bzrtp_cache_write_lock((void *)aliceDB, zuidalicebob, "zrtp", patternColNames, (uint8_t **)patternColValues, patternColValuesLength, patternLength, NULL), 0, int, "%x"); /* Try to write a zuid row in zrtp table while zuid is not present in ziduri table: it shall fail */ BC_ASSERT_EQUAL(bzrtp_cache_write_lock((void *)aliceDB, zuidalicebob+10, "zrtp", patternColNames, (uint8_t **)patternColValues, patternColValuesLength, patternLength, NULL), BZRTP_ZIDCACHE_UNABLETOUPDATE, int, "%x"); /* Now read the data and check they're the same */ BC_ASSERT_EQUAL(bzrtp_cache_read_lock((void *)aliceDB, zuidalicebob, "zrtp", patternColNames, readValues, readLength, patternLength+1, NULL), 0, int, "%x"); for (i=0; iRNGContext, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDalice, patternZIDalice, 12), 0, int, "%x"); /* test the getZuid function */ BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidalicebob, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(zuidalicebob, 5, int, "%d"); /* from the pattern DB: the zuid for alice/bob+provided ZID is 5 */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "ecila@sip.linphone.org", selfZIDecila, aliceContext->RNGContext, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDecila, patternZIDecila, 12), 0, int, "%x"); bzrtp_destroyBzrtpContext(aliceContext, 0); /* note: we didn't initialised any channel, so just give 0 to destroy, it will destroy the bzrtp context itself */ for (i=0; i<4; i++) { free(readValues[i]); } sqlite3_close(aliceDB); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif } static void test_cache_zrtpSecrets(void) { #ifdef ZIDCACHE_ENABLED bzrtpContext_t *aliceContext; sqlite3 *aliceDB=NULL; uint8_t peerZIDbob[12] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xed, 0xcb, 0xa9, 0x87,}; uint8_t patternRs1[16] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x12}; uint8_t patternRs2[32] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; uint8_t patternAux[27] = {0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x78, 0x69, 0x6c, 0x69, 0x61, 0x72, 0x79, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74}; char patternFilename[1024]; char *resource_dir = (char *)bc_tester_get_resource_dir_prefix(); int ret; /* we need a bzrtp context */ aliceContext = bzrtp_createBzrtpContext(); /* open the pattern file and set it in the zrtp context as zidcache db */ sprintf(patternFilename, "%s/patternZIDAlice.sqlite", resource_dir); BC_ASSERT_EQUAL((ret = bzrtptester_sqlite3_open(patternFilename, &aliceDB)), SQLITE_OK, int, "0x%x"); if (ret != SQLITE_OK) { bzrtp_message("Error: unable to find patternZIDAlice.sqlite file. Did you set correctly the --resource-dir argument(current set: %s)", resource_dir==NULL?"NULL":resource_dir); return; } BC_ASSERT_EQUAL(bzrtp_setZIDCache(aliceContext, (void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org"),0,int,"%x"); /* get Secrets */ BC_ASSERT_EQUAL(bzrtp_getPeerAssociatedSecrets(aliceContext, peerZIDbob), 0, int, "%x"); BC_ASSERT_EQUAL(aliceContext->zuid, 5, int, "%d"); BC_ASSERT_EQUAL(aliceContext->cachedSecret.rs1Length, 16, int, "%d"); BC_ASSERT_EQUAL(memcmp(aliceContext->cachedSecret.rs1,patternRs1, 16), 0, int, "%d"); BC_ASSERT_EQUAL(aliceContext->cachedSecret.rs2Length, 32, int, "%d"); BC_ASSERT_EQUAL(memcmp(aliceContext->cachedSecret.rs2,patternRs2, 32), 0, int, "%d"); BC_ASSERT_EQUAL(aliceContext->cachedSecret.auxsecretLength, 27, int, "%d"); BC_ASSERT_EQUAL(memcmp(aliceContext->cachedSecret.auxsecret,patternAux, 27), 0, int, "%d"); BC_ASSERT_EQUAL(aliceContext->cachedSecret.pbxsecretLength, 0, int, "%d"); BC_ASSERT_PTR_NULL(aliceContext->cachedSecret.pbxsecret); BC_ASSERT_EQUAL(aliceContext->cachedSecret.previouslyVerifiedSas, 1, int, "%d"); /* now try to retrieve secret for an unknow peer uri */ BC_ASSERT_EQUAL(bzrtp_setZIDCache(aliceContext, (void *)aliceDB, "alice@sip.linphone.org", "eve@sip.linphone.org"),0,int,"%x"); BC_ASSERT_EQUAL(bzrtp_getPeerAssociatedSecrets(aliceContext, peerZIDbob), 0, int, "%x"); BC_ASSERT_EQUAL(aliceContext->cachedSecret.rs1Length, 0, int, "%d"); BC_ASSERT_PTR_NULL(aliceContext->cachedSecret.rs1); BC_ASSERT_EQUAL(aliceContext->cachedSecret.rs2Length, 0, int, "%d"); BC_ASSERT_PTR_NULL(aliceContext->cachedSecret.rs2); BC_ASSERT_EQUAL(aliceContext->cachedSecret.auxsecretLength, 0, int, "%d"); BC_ASSERT_PTR_NULL(aliceContext->cachedSecret.auxsecret); BC_ASSERT_EQUAL(aliceContext->cachedSecret.pbxsecretLength, 0, int, "%d"); BC_ASSERT_PTR_NULL(aliceContext->cachedSecret.pbxsecret); BC_ASSERT_EQUAL(aliceContext->cachedSecret.previouslyVerifiedSas, 0, int, "%d"); bzrtp_destroyBzrtpContext(aliceContext, 0); /* note: we didn't initialised any channel, so just give 0 to destroy, it will destroy the bzrtp context itself */ sqlite3_close(aliceDB); #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } static void test_cache_migration(void) { #ifdef ZIDCACHE_ENABLED #ifdef HAVE_LIBXML2 uint8_t pattern_selfZIDalice[12] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb}; uint8_t selfZIDalice[12]; char *aliceCacheFile = bc_tester_file("tmpZIDAlice_cache_migration.sqlite");; sqlite3 *aliceDB=NULL; /* Parse the xmlCache */ xmlDocPtr cacheXml = xmlParseDoc((xmlChar*)xmlCacheMigration); /* create a new DB file */ remove(aliceCacheFile); bzrtptester_sqlite3_open(aliceCacheFile, &aliceDB); BC_ASSERT_EQUAL(bzrtp_initCache_lock((void *)aliceDB, NULL), BZRTP_CACHE_SETUP, int, "%x"); /* perform migration */ BC_ASSERT_EQUAL(bzrtp_cache_migration((void *)cacheXml, (void *)aliceDB, "sip:alice@sip.linphone.org"), 0, int, "%x"); /* check values in new cache */ BC_ASSERT_EQUAL(bzrtp_getSelfZID_lock(aliceDB, "sip:alice@sip.linphone.org", selfZIDalice, NULL, NULL), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(pattern_selfZIDalice, selfZIDalice, 12), 0, int, "%d"); /* TODO: read values from sql cache lime and zrtp tables and check they are the expected ones */ /* cleaning */ sqlite3_close(aliceDB); xmlFreeDoc(cacheXml); xmlCleanupParser(); remove(aliceCacheFile); bc_free(aliceCacheFile); #else /* HAVE_LIBXML2 */ bzrtp_message("Test skipped as LibXML2 not present\n"); #endif /* HAVE_LIBXML2 */ #else /* ZIDCACHE_ENABLED */ bzrtp_message("Test skipped as ZID cache is disabled\n"); #endif /* ZIDCACHE_ENABLED */ } static test_t zidcache_tests[] = { TEST_NO_TAG("SelfZID", test_cache_getSelfZID), TEST_NO_TAG("ZRTP secrets", test_cache_zrtpSecrets), TEST_NO_TAG("Migration", test_cache_migration), }; test_suite_t zidcache_test_suite = { "ZID Cache", NULL, NULL, NULL, NULL, sizeof(zidcache_tests) / sizeof(zidcache_tests[0]), zidcache_tests }; bzrtp-4.4.13/test/patternZIDAlice.sqlite000066400000000000000000000240001364144501400201260ustar00rootroot00000000000000SQLite format 3@ : 9:.$  i,;$95alice@sip.linphone.orgeve@sip.linphone.org;$95"3DUfwalice@sip.linphone.orgbob@sip.linphone.org;$95"3DUfwecila@sip.linphone.orgbob@sip.linphone.org;$95#Eg˩alice@sip.linphone.orgbob@sip.linphone.org+$9rkW==cecila@sip.linphone.orgself+$9B1kiralice@sip.linphone.orgself  ziduri ggA,47""33DDUUffwwwwwwwwwwwwthis is also a secretT,LB4VxܺvT2UUUUUUUUUUUUUUUUthis is an auxiliary secret     /zz+?indexsqlite_autoindex_ziduri_1ziduri}UtableziduriziduriCREATE TABLE "ziduri" ( `zuid` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, `zid` BLOB NOT NULL DEFAULT '00000000000', `selfuri` TEXT NOT NULL DEFAULT 'unset', `peeruri` TEXT NOT NULL DEFAULT 'unset' , active INTEGER DEFAULT 0)P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)  !!yU& tablelimelimeCREATE TABLE lime (zuid INTEGER NOT NULL DEFAULT 0 UNIQUE,sndKey BLOB DEFAULT NULL,rcvKey BLOB DEFAULT NULL,sndSId BLOB DEFAULT NULL,rcvSId BLOB DEFAULT NULL,sndIndex BLOB DEFAULT NULL,rcvIndex BLOB DEFAULT NULL,valid BLOB DEFAULT NULL,FOREIGN KEY(zuid) REFERENCES ziduri(zuid) ON UPDATE CASCADE ON DELETE CASCADE)'';indexsqlite_autoindex_lime_1lime '%;indexsqlite_autoindex_zrtp_1zrtp.$?tablezrtpzrtpCREATE TABLE "zrtp" ( `zuid` INTEGER NOT NULL DEFAULT 0 UNIQUE, `rs1` BLOB DEFAULT NULL, `rs2` BLOB DEFAULT NULL, `aux` BLOB DEFAULT NULL, `pbx` BLOB DEFAULT NULL, `pvs` BLOB DEFAULT NULL, FOREIGN KEY(`zuid`) REFERENCES `ziduri`(`zuid`) ON UPDATE CASCADE ON DELETE CASCADE )bzrtp-4.4.13/test/testUtils.c000066400000000000000000000321151364144501400160730ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "testUtils.h" #include "cryptoUtils.h" #include "bzrtpTest.h" #ifndef _WIN32 #if !defined(__QNXNTO__) && !(defined(__ANDROID__) && defined(__LP64__)) #include #include #endif #else #include #endif int verbose = 0; /* trace functions: bzrtp algo code to string */ const char *bzrtp_hash_toString(uint8_t hashAlgo) { switch(hashAlgo) { case(ZRTP_UNSET_ALGO): return "unset"; case(ZRTP_HASH_S256): return "SHA-256"; case(ZRTP_HASH_S384): return "SHA-384"; case(ZRTP_HASH_N256): return "SHA3-256"; case(ZRTP_HASH_N384): return "SHA3-384"; default: return "Unknown Algo"; } } const char *bzrtp_keyAgreement_toString(uint8_t keyAgreementAlgo) { switch(keyAgreementAlgo) { case(ZRTP_UNSET_ALGO): return "unset"; case(ZRTP_KEYAGREEMENT_DH2k): return "DHM-2048"; case(ZRTP_KEYAGREEMENT_EC25): return "ECDH-256"; case(ZRTP_KEYAGREEMENT_DH3k): return "DHM-3072"; case(ZRTP_KEYAGREEMENT_EC38): return "ECDH-384"; case(ZRTP_KEYAGREEMENT_EC52): return "ECDH-521"; case(ZRTP_KEYAGREEMENT_X255): return "X25519"; case(ZRTP_KEYAGREEMENT_X448): return "X448"; case(ZRTP_KEYAGREEMENT_Prsh): return "PreShared"; case(ZRTP_KEYAGREEMENT_Mult): return "MultiStream"; default: return "Unknown Algo"; } } const char *bzrtp_cipher_toString(uint8_t cipherAlgo) { switch(cipherAlgo) { case(ZRTP_UNSET_ALGO): return "unset"; case(ZRTP_CIPHER_AES1): return "AES-128"; case(ZRTP_CIPHER_AES2): return "AES-192"; case(ZRTP_CIPHER_AES3): return "AES-256"; case(ZRTP_CIPHER_2FS1): return "TwoFish-128"; case(ZRTP_CIPHER_2FS2): return "TwoFish-192"; case(ZRTP_CIPHER_2FS3): return "TwoFish-256"; default: return "Unknown Algo"; } } const char *bzrtp_authtag_toString(uint8_t authtagAlgo) { switch(authtagAlgo) { case(ZRTP_UNSET_ALGO): return "unset"; case(ZRTP_AUTHTAG_HS32): return "HMAC-SHA1-32"; case(ZRTP_AUTHTAG_HS80): return "HMAC-SHA1-80"; case(ZRTP_AUTHTAG_SK32): return "Skein-32"; case(ZRTP_AUTHTAG_SK64): return "Skein-64"; default: return "Unknown Algo"; } } const char *bzrtp_sas_toString(uint8_t sasAlgo) { switch(sasAlgo) { case(ZRTP_UNSET_ALGO): return "unset"; case(ZRTP_SAS_B32): return "Base32"; case(ZRTP_SAS_B256): return "PGP-WordList"; default: return "Unknown Algo"; } } void printHex(char *title, uint8_t *data, uint32_t length) { if (verbose) { uint32_t i; printf ("%s : ", title); for (i=0; isourceIdentifier, zrtpPacket->messageLength); if (zrtpPacket->packetString!=NULL) { /* paquet has been built and we need to get his sequence number in the packetString */ printf ("Sequence number: %02x%02x", *(zrtpPacket->packetString+2), *(zrtpPacket->packetString+3)); } else { /* packet has been parsed, so get his sequence number in the structure */ printf ("Sequence number: %04x", zrtpPacket->sequenceNumber); } switch (zrtpPacket->messageType) { case MSGTYPE_HELLO : { bzrtpHelloMessage_t *messageData; uint8_t algoTypeString[4]; printf(" - Message Type : Hello\n"); messageData = (bzrtpHelloMessage_t *)zrtpPacket->messageData; printf ("Version %.4s\nIdentifier %.16s\n", messageData->version, messageData->clientIdentifier); printHex ("H3", messageData->H3, 32); printHex ("ZID", messageData->ZID, 12); printf ("S : %d - M : %d - P : %d\nhc : %x - cc : %x - ac : %x - kc : %x - sc : %x\n", messageData->S, messageData->M, messageData->P, messageData->hc, messageData->cc, messageData->ac, messageData->kc, messageData->sc); printf ("hc "); for (j=0; jhc; j++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedHash[j], algoTypeString); printf("%.4s, ", algoTypeString); } printf ("\ncc "); for (j=0; jcc; j++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedCipher[j], algoTypeString); printf("%.4s, ", algoTypeString); } printf ("\nac "); for (j=0; jac; j++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedAuthTag[j], algoTypeString); printf("%.4s, ", algoTypeString); } printf ("\nkc "); for (j=0; jkc; j++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedKeyAgreement[j], algoTypeString); printf("%.4s, ", algoTypeString); } printf ("\nsc "); for (j=0; jsc; j++) { bzrtp_cryptoAlgoTypeIntToString(messageData->supportedSas[j], algoTypeString); printf("%.4s, ", algoTypeString); } printHex("\nMAC", messageData->MAC, 8); } break; /* MSGTYPE_HELLO */ case MSGTYPE_HELLOACK : { printf(" - Message Type : Hello ACK\n"); } break; case MSGTYPE_COMMIT: { uint8_t algoTypeString[4]; bzrtpCommitMessage_t *messageData; printf(" - Message Type : Commit\n"); messageData = (bzrtpCommitMessage_t *)zrtpPacket->messageData; printHex("H2", messageData->H2, 32); printHex("ZID", messageData->ZID, 12); bzrtp_cryptoAlgoTypeIntToString(messageData->hashAlgo, algoTypeString); printf("Hash Algo: %.4s\n", algoTypeString); bzrtp_cryptoAlgoTypeIntToString(messageData->cipherAlgo, algoTypeString); printf("Cipher Algo: %.4s\n", algoTypeString); bzrtp_cryptoAlgoTypeIntToString(messageData->authTagAlgo, algoTypeString); printf("Auth tag Algo: %.4s\n", algoTypeString); bzrtp_cryptoAlgoTypeIntToString(messageData->keyAgreementAlgo, algoTypeString); printf("Key agreement Algo: %.4s\n", algoTypeString); bzrtp_cryptoAlgoTypeIntToString(messageData->sasAlgo, algoTypeString); printf("Sas Algo: %.4s\n", algoTypeString); /* if it is a multistream or preshared commit, get the 16 bytes nonce */ if ((messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) || (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Mult)) { printHex("Nonce", messageData->nonce, 16); /* and the keyID for preshared commit only */ if (messageData->keyAgreementAlgo == ZRTP_KEYAGREEMENT_Prsh) { printHex("KeyId", messageData->keyID, 8); } } else { /* it's a DH commit message, get the hvi */ printHex("hvi", messageData->hvi, 32); } printHex("\nMAC", messageData->MAC, 8); } break; case MSGTYPE_DHPART1: case MSGTYPE_DHPART2: { bzrtpDHPartMessage_t *messageData; if (zrtpPacket->messageType == MSGTYPE_DHPART1) { printf(" - Message Type : DHPart1\n"); } else { printf(" - Message Type : DHPart2\n"); } messageData = (bzrtpDHPartMessage_t *)zrtpPacket->messageData; printHex ("H1", messageData->H1, 32); printHex ("rs1ID", messageData->rs1ID, 8); printHex ("rs2ID", messageData->rs2ID, 8); printHex ("auxsecretID", messageData->auxsecretID, 8); printHex ("pbxsecretID", messageData->pbxsecretID, 8); printHex ("rs1ID", messageData->rs1ID, 8); printf("PV length is %d\n", (zrtpPacket->messageLength-84)); printHex ("PV", messageData->pv, (zrtpPacket->messageLength-84)); /* length of fixed part of the message is 84, rest is the variable length PV */ printHex("MAC", messageData->MAC, 8); } break; case MSGTYPE_CONFIRM1: case MSGTYPE_CONFIRM2: { bzrtpConfirmMessage_t *messageData; if (zrtpPacket->messageType == MSGTYPE_CONFIRM1) { printf(" - Message Type : Confirm1\n"); } else { printf(" - Message Type : Confirm2\n"); } messageData = (bzrtpConfirmMessage_t *)zrtpPacket->messageData; printHex("H0", messageData->H0, 32); printf("sig_len %d\n", messageData->sig_len); printf("E %d V %d A %d D %d\n", messageData->E, messageData->V, messageData->A, messageData->D); printf("Cache expiration Interval %08x\n", messageData->cacheExpirationInterval); } break; case MSGTYPE_CONF2ACK: { printf(" - Message Type: Conf2ACK\n"); } } if (addRawMessage) { printHex("Data", zrtpPacket->packetString, zrtpPacket->messageLength+16); } fflush(NULL); } } void dumpContext(char *title, bzrtpContext_t *zrtpContext) { if (verbose) { uint8_t buffer[4]; int i,j; printf("%s context is :\n", title); printHex("selfZID", zrtpContext->selfZID, 12); printHex("peerZID", zrtpContext->peerZID, 12); for (i=0; ichannelContext[i] != NULL) { bzrtpChannelContext_t *channelContext = zrtpContext->channelContext[i]; printf("Channel %i\n self: %08x\n", i, channelContext->selfSSRC); printf (" selfH: "); for (j=0; j<4; j++) { printHex(" ", channelContext->selfH[j], 32); } printf (" peerH: "); for (j=0; j<4; j++) { printHex(" ", channelContext->peerH[j], 32); } bzrtp_cryptoAlgoTypeIntToString(channelContext->hashAlgo, buffer); printf(" Selected algos\n - Hash: %.4s\n", buffer); bzrtp_cryptoAlgoTypeIntToString(channelContext->cipherAlgo, buffer); printf(" - cipher: %.4s\n", buffer); bzrtp_cryptoAlgoTypeIntToString(channelContext->authTagAlgo, buffer); printf(" - auth tag: %.4s\n", buffer); bzrtp_cryptoAlgoTypeIntToString(channelContext->keyAgreementAlgo, buffer); printf(" - key agreement: %.4s\n", buffer); bzrtp_cryptoAlgoTypeIntToString(channelContext->sasAlgo, buffer); printf(" - sas: %.4s\n", buffer); printHex(" initiator auxID", channelContext->initiatorAuxsecretID, 8); printHex(" responder auxID", channelContext->responderAuxsecretID, 8); if (channelContext->s0 != NULL) { printHex(" s0", channelContext->s0, channelContext->hashLength); } if(channelContext->srtpSecrets.sas != NULL) { printf(" sas : %.4s\n", channelContext->srtpSecrets.sas); } if (channelContext->srtpSecrets.selfSrtpKey != NULL) { printHex(" selfsrtp key", channelContext->srtpSecrets.selfSrtpKey, channelContext->srtpSecrets.selfSrtpKeyLength); printHex(" selfsrtp salt", channelContext->srtpSecrets.selfSrtpSalt, channelContext->srtpSecrets.selfSrtpSaltLength); printHex(" peersrtp key", channelContext->srtpSecrets.peerSrtpKey, channelContext->srtpSecrets.peerSrtpKeyLength); printHex(" peersrtp salt", channelContext->srtpSecrets.peerSrtpSalt, channelContext->srtpSecrets.peerSrtpSaltLength); } if (channelContext->mackeyi!=NULL) { printHex(" mackeyi", channelContext->mackeyi, channelContext->hashLength); } if (channelContext->mackeyr!=NULL) { printHex(" mackeyr", channelContext->mackeyr, channelContext->hashLength); } if (channelContext->zrtpkeyi!=NULL) { printHex(" zrtpkeyi", channelContext->zrtpkeyi, channelContext->cipherKeyLength); } if (channelContext->zrtpkeyr!=NULL) { printHex(" zrtpkeyr", channelContext->zrtpkeyr, channelContext->cipherKeyLength); } } } printf("Initiator Shared Secrets :\n"); printHex("rs1ID", zrtpContext->initiatorCachedSecretHash.rs1ID, 8); printHex("rs2ID", zrtpContext->initiatorCachedSecretHash.rs2ID, 8); printHex("pbxID", zrtpContext->initiatorCachedSecretHash.pbxsecretID, 8); printf("Responder Shared Secrets :\n"); printHex("rs1ID", zrtpContext->responderCachedSecretHash.rs1ID, 8); printHex("rs2ID", zrtpContext->responderCachedSecretHash.rs2ID, 8); printHex("pbxID", zrtpContext->responderCachedSecretHash.pbxsecretID, 8); fflush(NULL); } } #ifdef ZIDCACHE_ENABLED #define MAX_PATH_SIZE 1024 int bzrtptester_sqlite3_open(const char *db_file, sqlite3 **db) { char* errmsg = NULL; int ret; int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; #if TARGET_OS_IPHONE /* the secured filesystem of the iPHone doesn't allow writing while the app is in background mode, which is problematic. * We workaround by asking that the open is made with no protection*/ flags |= SQLITE_OPEN_FILEPROTECTION_NONE; #endif ret = sqlite3_open_v2(db_file, db, flags, NULL); if (ret != SQLITE_OK) return ret; // Some platforms do not provide a way to create temporary files which are needed // for transactions... so we work in memory only // see http ://www.sqlite.org/compile.html#temp_store ret = sqlite3_exec(*db, "PRAGMA temp_store=MEMORY", NULL, NULL, &errmsg); if (ret != SQLITE_OK) { sqlite3_free(errmsg); } return ret; } #endif /* ZIDCACHE_ENABLED */ bzrtp-4.4.13/test/testUtils.h000066400000000000000000000026201364144501400160760ustar00rootroot00000000000000/* * Copyright (c) 2014-2019 Belledonne Communications SARL. * * This file is part of bzrtp. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "typedef.h" #include "packetParser.h" extern int verbose; void printHex(char *title, uint8_t *data, uint32_t length); void packetDump(bzrtpPacket_t *zrtpPacket, uint8_t addRawMessage); void dumpContext(char *title, bzrtpContext_t *zrtpContext); const char *bzrtp_hash_toString(uint8_t hashAlgo); const char *bzrtp_keyAgreement_toString(uint8_t keyAgreementAlgo); const char *bzrtp_cipher_toString(uint8_t cipherAlgo); const char *bzrtp_authtag_toString(uint8_t authtagAlgo); const char *bzrtp_sas_toString(uint8_t sasAlgo); #ifdef ZIDCACHE_ENABLED int bzrtptester_sqlite3_open(const char *db_file, sqlite3 **db); #endif /* ZIDCACHE_ENABLED */