pax_global_header00006660000000000000000000000064143456664310014526gustar00rootroot0000000000000052 comment=6dda64d0f3e5f98dd6bff21871031668d30c8bdd bctoolbox-5.2.0/000077500000000000000000000000001434566643100135255ustar00rootroot00000000000000bctoolbox-5.2.0/.github/000077500000000000000000000000001434566643100150655ustar00rootroot00000000000000bctoolbox-5.2.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001434566643100172505ustar00rootroot00000000000000bctoolbox-5.2.0/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000134721434566643100221520ustar00rootroot00000000000000--- name: Bug report description: File a bug/issue title: "[Bug]: " labels: ["bug"] body: - type: markdown attributes: value: '# Reminder' - type: markdown attributes: value: | The responses are provided by the **community** and, on a **best effort** basis, by some Belledonne Communications SARL engineers working on Linphone and its related projects. The community means any people all around the world simply willing to participate to the discussions. Belledonne Communications SARL **disclaims any WARRANTY** that the content posted on github issues or mailing lists is technically correct. Responses from Belledonne Communications SARL engineers shall be considered as individual contributions and shall not be seen as Belledonne Communications's official point of view or commitment. The Github issue tracker must be seen as a place for **collaboration**. Issues submitted should be of general interest, in the goal of improving the software. Consider that a **well documented** issue (with precise reproduction procedure, logs, stack trace if relevant, possibly a corrective patch) has a higher chance to receive interest and feedback from community members and Belledonne Communications' engineers. __Issues poorly documented, with no facts, or asking for debugging assistance for a custom app using Linphone's libraries, or for a modified version of Linphone are unlikely to receive any kind of response.__ People using Linphone or its related projects within the scope of their company job are invited to contact [Belledonne Communications](https://linphone.org/contact#content-bottom3) in order to obtain commercial support. - type: markdown attributes: value: | # Well ordered issues are treated issues **In our apps, the [Linphone-SDK](https://github.com/BelledonneCommunications/linphone-sdk) is used.** Please report your issue here **ONLY** if you are sure that the origin of the error is in this module. Otherwise, open an issue in the repository of the app you are using or in the Linphone-SDK, and we will move it to the related module. - type: markdown attributes: value: | # Useful links [Linphone.org](https://linphone.org) [Linphone commercial contact](https://linphone.org/contact#content-bottom3) Linphone Vulnerability/Security contact: vulnerabilities@linphone.org [Contributor agreement (to sign and to return to sales@belledonne-communications.com for a pull request)](https://linphone.org/sites/default/files/bc-contributor-agreement_0.pdf) - type: textarea attributes: label: | Context description: | - For which purpose do you use the project ? - With which software/hardware it is integrated ? - Did you use sip.linphone.org or a different SIP service (in this case specify which one and which version) ? value: | I use this project in a custom app running on Linux with the sip.linphone.org service for my company. I want to do a simple call between an Android phone and a Linux client. There is an error with a method of this project and I'm sure that I followed the documentation and double checked before posting. validations: required: true - type: textarea attributes: label: General information description: | Complete it multiple time if there are multiple devices involved. Please note that the issue has more chances to be read if you report a bug seen in the latest version of the module. Ex: - Device: [e.g. Samsung Note 20 Ultra] - OS: [e.g. Android 11] - Version of the App [e.g. 4.3.1] - Version of the SDK [e.g 4.4.16] value: | - Device: - OS: - Version of the App: - Version of the SDK: validations: required: true - type: textarea attributes: label: Expected behaviour description: "A clear and concise description of what you expected to happen." value: | I wanted to do a simple call with the Linux client calling the Android phone. However, the phone doesn't ring when it is asleep. validations: required: true - type: textarea attributes: label: To Reproduce description: "Steps to reproduce the behavior:" value: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea attributes: label: 'Additional context' value: Add any other context about the problem here. - type: markdown attributes: value: | # Logs ## Module logs Enable debug logs in advanced section of the settings, restart the app, reproduce the issue and then go to About page, click on "Send logs" and copy/paste the link here. If you doesn't have such an option, just provide the logs in attachments. - type: input attributes: label: 'SDK logs URL' - type: markdown attributes: value: | ## Module crash logs In case of a crash related to this module, please also provide the backtrace of the crash in attachments using adb logcat (Android) or the device console (iOS). For desktop versions, you can get the backtrace from a core dump. - type: markdown attributes: value: | # Screenshots Please add screenshots in attachments to help us to understand your problem. - type: markdown attributes: value: | # Pcap file If this is a network issue, join a pcap file of your attempt in attachments (done with Wireshark or TCPDump, for example) bctoolbox-5.2.0/.gitignore000066400000000000000000000000261434566643100155130ustar00rootroot00000000000000bctoolbox.spec .*.swp bctoolbox-5.2.0/CHANGELOG.md000066400000000000000000000033661434566643100153460ustar00rootroot00000000000000# 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] ## [5.2.0] - 2022-11-14 ## Added - Crypto: add support for post-quantum algorithms. - Crash handler and backtrace logger for Windows. ## Changed - minor changes. ## [5.1.0] - 2022-02-14 ### Added - new vfs read2/write2 functions without offset parameter, to easy mapping with standard libc functions. ### Changed - optimized C++ logging macros. ## [5.0.0] - 2021-07-08 ### Added - Tester API: add API to set maximum number of failed tests. ### Fixed - Few bugfixes (see git log) ## [4.5.0] - 2021-03-29 ### Added - Encrypted VFS API. ### Changed - Callback for client certificate query is now passed with the full list of subjects. - miscellaneous small things, see git commits for details. ## [4.4.0] - 2020-6-09 ### Changed - Version number now follows linphone-sdk's versionning. ### Removed - Polarssl 1.2 support. ## [0.6.0] - 2017-07-20 ### Added - Add API to escape/unescape strings (SIP, VCARD). ## [0.5.1] - 2017-02-22 ### Added - "const char * to void *" map feature ### Fixed - security bugfix: TLS session could be successfully established whereas the common name did not match the server name. ## [0.2.0] - 2016-08-08 ### Added - Creating a Virtual File System bctbx_vfs allowing direct file access and I/Os. - integrate OS abstraction layer, list API, logging API from oRTP - integrate getaddrinfo() abstraction api, in order to provide consistent getaddrinfo() support on all platforms. ## [0.0.1] - 2016-01-01 ### Added - Initial release. ### Changed ### Removed bctoolbox-5.2.0/CMakeLists.txt000066400000000000000000000171301434566643100162670ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2010-2021 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.2) # CMP0077 policy is required by Flexisip build. Remove it once the CMake required # version is higer or equal to 3.13. if(NOT CMAKE_VERSION VERSION_LESS 3.13) cmake_policy(SET CMP0077 NEW) endif() project(bctoolbox VERSION 5.2.0) set(BCTOOLBOX_VERSION "${PROJECT_VERSION}") set(BCTOOLBOX_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(BCTOOLBOX_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(BCTOOLBOX_VERSION_PATCH ${PROJECT_VERSION_PATCH}) set(BCTOOLBOX_SO_VERSION 1) set(BCTOOLBOXTESTER_SO_VERSION 1) option(ENABLE_SHARED "Build shared library." ON) option(ENABLE_STATIC "Build static library." ON) option(ENABLE_POLARSSL "Enable polarssl support" ON) option(ENABLE_MBEDTLS "Enable mabedtls support" ON) option(ENABLE_DECAF "Enable Elliptic Curve Cryptography support" ON) option(ENABLE_STRICT "Pass strict flags to the compiler" ON) option(ENABLE_TESTS_COMPONENT "Enable compilation of tests helper library" ON) option(ENABLE_TESTS "Enable compilation of tests" ON) option(ENABLE_PACKAGE_SOURCE "Create 'package_source' target for source archive making (CMake >= 3.11)" OFF) option(ENABLE_DEFAULT_LOG_HANDLER "A default log handler will be initialized, if OFF no logging will be done before you initialize one." ON) # Hidden non-cache options: # * DISABLE_BC_PACKAGE_SEARCH: skip find_package() for every BC package (bctoolbox, ortp, etc.) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS NO) include(CheckLibraryExists) include(CheckSymbolExists) include(CheckCSourceCompiles) include(CMakePushCheckState) include(GNUInstallDirs) include(cmake/BcToolboxCMakeUtils.cmake) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") if(NOT CPACK_GENERATOR AND 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() if(APPLE) find_package(Iconv REQUIRED) endif() if(ENABLE_MBEDTLS) message(STATUS "MBEDTLS Enabled") if(NOT DISABLE_MBEDTLS_SEARCH) message(STATUS "MBEDTLS Enabled search enabled") find_package(MbedTLS) else() message(STATUS "MBEDTLS Enabled search disabled") set (MBEDTLS_FOUND On) set (DTLS_SRTP_AVAILABLE On) # we using internal Mbedtls lib, we always have DTLS-SRTP endif() if(MBEDTLS_FOUND) message(STATUS "Using mbedTLS") set(HAVE_MBEDTLS 1) set(MBEDTLS_TARGETS mbedtls mbedx509 mbedcrypto ) endif() endif() if(ENABLE_DECAF) find_package(Decaf CONFIG) if(DECAF_FOUND) message(STATUS "Using Decaf") set(HAVE_DECAF 1) endif() endif() if(ENABLE_POLARSSL AND NOT MBEDTLS_FOUND) find_package(PolarSSL REQUIRED) if(POLARSSL_FOUND) message(STATUS "Using polarSSL") set(HAVE_POLARSSL 1) if(CTR_DRBG_FREE) set(HAVE_CTR_DRGB_FREE 1) endif() endif() endif() if(DTLS_SRTP_AVAILABLE) message(STATUS "DTLS SRTP available") set(HAVE_DTLS_SRTP 1) else() message(STATUS "DTLS SRTP not available") endif() if(POLARSSL_FOUND) get_filename_component(polarssl_library_path "${POLARSSL_LIBRARIES}" PATH) set(LIBS_PRIVATE "${LIBS_PRIVATE} -L${polarssl_library_path} -lpolarssl") endif() if (ENABLE_TESTS_COMPONENT) if (NOT DISABLE_BC_PACKAGE_SEARCH) find_package(BcUnit CONFIG REQUIRED) cmake_push_check_state(RESET) list(APPEND CMAKE_REQUIRED_INCLUDES ${BCUNIT_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${BCUNIT_LIBRARIES}) check_symbol_exists("CU_get_suite" "BCUnit/BCUnit.h" HAVE_CU_GET_SUITE) check_symbol_exists("CU_curses_run_tests" "BCUnit/BCUnit.h" HAVE_CU_CURSES) check_symbol_exists("CU_set_trace_handler" "BCUnit/Util.h" HAVE_CU_SET_TRACE_HANDLER) cmake_pop_check_state() set(TESTER_REQUIRES_PRIVATE "bcunit") else () set(HAVE_CU_GET_SUITE ON) set(HAVE_CU_SET_TRACE_HANDLER ON) set(TESTER_REQUIRES_PRIVATE "bcunit") endif () endif () configure_file(${CMAKE_CURRENT_SOURCE_DIR}/bctoolbox.pc.in ${CMAKE_CURRENT_BINARY_DIR}/bctoolbox.pc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/bctoolbox.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) if(ENABLE_TESTS_COMPONENT) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/bctoolbox-tester.pc.in ${CMAKE_CURRENT_BINARY_DIR}/bctoolbox-tester.pc) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/bctoolbox-tester.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) endif() find_package(Threads REQUIRED) include_directories( include src ${CMAKE_CURRENT_BINARY_DIR} ) set(BCTOOLBOX_CPPFLAGS ) if(ENABLE_STATIC) set(BCTBX_STATIC 1) list(APPEND BCTOOLBOX_CPPFLAGS "-DBCTBX_STATIC") endif() bc_init_compilation_flags(STRICT_OPTIONS_CPP STRICT_OPTIONS_C STRICT_OPTIONS_CXX ENABLE_STRICT) if(CMAKE_VERSION VERSION_LESS 3.1 AND NOT APPLE AND NOT MSVC) list(APPEND STRICT_OPTIONS_CXX "-std=c++11") endif() set(EXPORT_TARGETS_NAME "bctoolbox") check_library_exists("rt" "clock_gettime" "" HAVE_LIBRT) check_library_exists("dl" "dladdr" "" HAVE_LIBDL) if(ANDROID) set(HAVE_EXECINFO 0) else() check_include_file("execinfo.h" HAVE_EXECINFO) 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") add_subdirectory(include) add_subdirectory(src) if(ENABLE_TESTS AND ENABLE_TESTS_COMPONENT) add_subdirectory(tester) endif() if(ENABLE_PACKAGE_SOURCE) add_subdirectory(build) 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 ) configure_package_config_file(cmake/BcToolboxConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}Config.cmake" INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION} NO_SET_AND_CHECK_MACRO ) configure_file(cmake/BcToolboxCMakeUtils.cmake "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}CMakeUtils.cmake" COPYONLY) configure_file(cmake/BcGitVersion.cmake "${CMAKE_CURRENT_BINARY_DIR}/BcGitVersion.cmake" COPYONLY) configure_file(cmake/gitversion.h.in "${CMAKE_CURRENT_BINARY_DIR}/gitversion.h.in" COPYONLY) install(EXPORT ${EXPORT_TARGETS_NAME}Targets FILE "${EXPORT_TARGETS_NAME}Targets.cmake" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) install(FILES "cmake/MakeArchive.cmake" "cmake/ConfigureSpecfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}ConfigVersion.cmake" "${CMAKE_CURRENT_BINARY_DIR}/${EXPORT_TARGETS_NAME}CMakeUtils.cmake" "${CMAKE_CURRENT_BINARY_DIR}/BcGitVersion.cmake" "${CMAKE_CURRENT_BINARY_DIR}/gitversion.h.in" DESTINATION ${CONFIG_PACKAGE_LOCATION} ) bctoolbox-5.2.0/LICENSE.txt000066400000000000000000000773311434566643100153630ustar00rootroot00000000000000 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 bctoolbox-5.2.0/README.md000066400000000000000000000040051434566643100150030ustar00rootroot00000000000000[![pipeline status](https://gitlab.linphone.org/BC/public/bctoolbox/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/bctoolbox/commits/master) BcToolbox ========= Utilities library used by Belledonne Communications softwares like belle-sip, mediastreamer2 and liblinphone. Depends ------- - **mbedtls[1]**: implementation of TLS interface of BcToolbox. For backward compatibility, support of polarssl is also provided. - **bcunit[2]** for unitary test tools. (optional) To compile ---------- cmake . -DCMAKE_INSTALL_PREFIX= -DCMAKE_PREFIX_PATH= make make install To make an rpm package ---------------------- cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCPACK_GENERATOR="RPM" make package Options ------- - `CMAKE_INSTALL_PREFIX=`: install prefix. - `CMAKE_PREFIX_PATH=`: search path prefix for dependencies e.g. mbedtls. - `ENABLE_MBEDTLS=NO`: do not look for mbedtls. Then, polarssl will be selected. - `ENABLE_POLARSSL=NO`: do not look for polarssl. That ensure to use mbedtls. - `ENABLE_SHARED=NO`: do not build the shared libraries. - `ENABLE_STATIC=NO`: do not build the static libraries. - `ENABLE_STRICT=NO`: do not build with strict compilator flags e.g. `-Wall -Werror`. - `ENABLE_TESTS=NO`: do not build testing binaries. - `ENABLE_TESTS_COMPONENT=NO`: do not build libbctoolbox-tester. Notes ----- For backward compatibility with distributions not having the required 2.8.12 cmake version, an automake/autoconf build system is also available. It is maintained as a best effort and then should be used only in last resort. Note 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. -------------------- - [1] - [2] git://git.linphone.org/bctoolbox.git or bctoolbox-5.2.0/bctoolbox-tester.pc.in000066400000000000000000000005601434566643100177560ustar00rootroot00000000000000# This is a comment prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ Name: bctoolbox-tester Description: A common set of tester wrappers used by Belledonne Communications's softwares Requires.private: @TESTER_REQUIRES_PRIVATE@ Version: @PROJECT_VERSION@ Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lbctoolbox-tester Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ bctoolbox-5.2.0/bctoolbox.pc.in000066400000000000000000000005111434566643100164460ustar00rootroot00000000000000# This is a comment prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ Name: bctoolbox Description: A common set of tools used by Belledonne Communication's softwares Version: @PROJECT_VERSION@ Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lbctoolbox Libs.private: @LIBS_PRIVATE@ Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ bctoolbox-5.2.0/build/000077500000000000000000000000001434566643100146245ustar00rootroot00000000000000bctoolbox-5.2.0/build/CMakeLists.txt000066400000000000000000000030021434566643100173570ustar00rootroot00000000000000############################################################################ # 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 set(BCTOOLBOX_CMAKE_DIR "${PROJECT_SOURCE_DIR}/cmake") set(BCTOOLBOX_CMAKE_UTILS "${BCTOOLBOX_CMAKE_DIR}/BcToolboxCMakeUtils.cmake") include("${BCTOOLBOX_CMAKE_DIR}/BcToolboxCMakeUtils.cmake") if(NOT CPACK_PACKAGE_NAME) set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") endif() set(CPACK_SOURCE_IGNORE_FILES "^${CMAKE_BINARY_DIR}" "^${PROJECT_SOURCE_DIR}/.git*" ) bc_make_package_source_target() bctoolbox-5.2.0/build/android/000077500000000000000000000000001434566643100162445ustar00rootroot00000000000000bctoolbox-5.2.0/build/android/Android-mbedtls.mk000066400000000000000000000006021434566643100216030ustar00rootroot00000000000000 LOCAL_PATH:= $(call my-dir)/../../src/ include $(CLEAR_VARS) LOCAL_MODULE:= libbctoolbox LOCAL_SRC_FILES := \ crypto/mbedtls.c LOCAL_CFLAGS += -Wno-maybe-uninitialized -DHAVE_DTLS_SRTP LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/ \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../../externals/mbedtls/include LOCAL_STATIC_LIBRARIES := \ mbedtls include $(BUILD_STATIC_LIBRARY) bctoolbox-5.2.0/build/android/Android-polarssl.mk000066400000000000000000000006051434566643100220130ustar00rootroot00000000000000 LOCAL_PATH:= $(call my-dir)/../../src/ include $(CLEAR_VARS) LOCAL_MODULE:= libbctoolbox LOCAL_SRC_FILES := \ crypto/polarssl.c LOCAL_CFLAGS += -Wno-maybe-uninitialized -DHAVE_DTLS_SRTP LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/ \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../../externals/polarssl/include LOCAL_STATIC_LIBRARIES := \ polarssl include $(BUILD_STATIC_LIBRARY) bctoolbox-5.2.0/build/android/Android-tester.mk000066400000000000000000000006241434566643100214630ustar00rootroot00000000000000LOCAL_PATH:= $(call my-dir)/../../src/ include $(CLEAR_VARS) LOCAL_MODULE:= libbctoolbox_tester LOCAL_SRC_FILES := \ tester/utils.c LOCAL_CFLAGS += -Wno-maybe-uninitialized -DIN_BCUNIT_SOURCES -DHAVE_CU_SET_TRACE_HANDLER=1 -DHAVE_CU_GET_SUITE=1 LOCAL_C_INCLUDES += \ $(LOCAL_PATH)/ \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../../externals/bcunit/BCUnit/Headers include $(BUILD_STATIC_LIBRARY) bctoolbox-5.2.0/build/osx/000077500000000000000000000000001434566643100154355ustar00rootroot00000000000000bctoolbox-5.2.0/build/osx/Info.plist.in000066400000000000000000000026111434566643100200120ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_FRAMEWORK_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_FRAMEWORK_IDENTIFIER} LSMinimumSystemVersion ${MIN_OS} MinimumOSVersion ${MIN_OS} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType FMWK CFBundleShortVersionString ${BCTOOLBOX_VERSION} CFBundleSignature ???? CFBundleVersion ${BCTOOLBOX_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} NSPrincipalClass NSApplication NSHighResolutionCapable True bctoolbox-5.2.0/build/rpm/000077500000000000000000000000001434566643100154225ustar00rootroot00000000000000bctoolbox-5.2.0/build/rpm/bctoolbox.spec.cmake000077500000000000000000000055231434566643100213600ustar00rootroot00000000000000# -*- 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: OS abstraction layer and utilities 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 Toolbox package used by Belledonne Communications projects %package devel Summary: Development libraries for bctoolbox Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Libraries and headers required to develop software with bctoolbox %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 LICENSE.txt CHANGELOG.md README.md %{_libdir}/*.so.* %files devel %defattr(-,root,root) %{_includedir}/bctoolbox %if @ENABLE_STATIC@ %{_libdir}/libbctoolbox.a %endif %if @ENABLE_SHARED@ %{_libdir}/libbctoolbox.so %endif %{_libdir}/pkgconfig/bctoolbox.pc %{_datadir}/bctoolbox/cmake/* %if @ENABLE_TESTS_COMPONENT@ %if @ENABLE_STATIC@ %{_libdir}/libbctoolbox-tester.a %endif %if @ENABLE_SHARED@ %{_libdir}/libbctoolbox-tester.so %endif %{_libdir}/pkgconfig/bctoolbox-tester.pc %endif %changelog * Tue Nov 27 2018 ronan.abhamon - Do not set CMAKE_INSTALL_LIBDIR and never with _libdir! * Tue Jan 16 2018 Ghislain MARY - Initial RPM release. bctoolbox-5.2.0/cmake/000077500000000000000000000000001434566643100146055ustar00rootroot00000000000000bctoolbox-5.2.0/cmake/BcGitVersion.cmake000066400000000000000000000041761434566643100201550ustar00rootroot00000000000000############################################################################ # BcGitVersion.cmake # Copyright (C) 2017 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. # ############################################################################ if(GIT_EXECUTABLE) macro(GIT_COMMAND OUTPUT_VAR) set(GIT_ARGS ${ARGN}) execute_process( COMMAND ${GIT_EXECUTABLE} ${ARGN} WORKING_DIRECTORY ${WORK_DIR} OUTPUT_VARIABLE ${OUTPUT_VAR} OUTPUT_STRIP_TRAILING_WHITESPACE ) endmacro() GIT_COMMAND(GIT_DESCRIBE describe --always) GIT_COMMAND(GIT_TAG describe --abbrev=0) GIT_COMMAND(GIT_REVISION rev-parse HEAD) endif() string(TOUPPER "${PROJECT_NAME}" PREFIX_GIT_VERSION) string(REPLACE "-" "_" PREFIX_GIT_VERSION "${PREFIX_GIT_VERSION}") if(GIT_DESCRIBE) if(NOT GIT_TAG STREQUAL PROJECT_VERSION) message(WARNING "Project version (${PROJECT_VERSION}) and git tag (${GIT_TAG}) differ. Please put them identical") endif() set(PROJECT_GIT_VERSION "${GIT_DESCRIBE}") configure_file("${TEMPLATE_DIR}/gitversion.h.in" "${OUTPUT_DIR}/gitversion.h" @ONLY) elseif(GIT_REVISION) set(PROJECT_GIT_VERSION "${LINPHONE_VERSION}_${GIT_REVISION}") configure_file("${TEMPLATE_DIR}/gitversion.h.in" "${OUTPUT_DIR}/gitversion.h" @ONLY) else() if(NOT EXISTS "${OUTPUT_DIR}/gitversion.h") execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${OUTPUT_DIR}/gitversion.h") endif() endif() bctoolbox-5.2.0/cmake/BcToolboxCMakeUtils.cmake000066400000000000000000000333761434566643100214400ustar00rootroot00000000000000############################################################################ # BcToolboxCMakeUtils.cmake # Copyright (C) 2010-2021 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(BCTOOLBOX_CMAKE_UTILS_DIR "${CMAKE_CURRENT_LIST_DIR}") macro(bc_init_compilation_flags CPP_FLAGS C_FLAGS CXX_FLAGS STRICT_COMPILATION) set(${CPP_FLAGS} ) set(${C_FLAGS} ) set(${CXX_FLAGS} ) if(MSVC) if(${STRICT_COMPILATION}) list(APPEND ${CPP_FLAGS} "/WX") list(APPEND STRICT_OPTIONS_CPP "/wd4996") # Disable deprecated function warnings endif() else() list(APPEND ${CPP_FLAGS} "-Wall" "-Wuninitialized") if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND ${CPP_FLAGS} "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-builtin-requires-header" "-Wno-unused-function" "-Wno-gnu-designator" "-Wno-array-bounds") elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU") list(APPEND ${CPP_FLAGS} "-Wno-error=pragmas") endif() if(APPLE) list(APPEND ${CPP_FLAGS} "-Wno-error=unknown-warning-option" "-Qunused-arguments" "-Wno-tautological-compare" "-Wno-unused-function" "-Wno-array-bounds") endif() if(ENABLE_STRICT) list(APPEND ${CPP_FLAGS} "-Werror" "-Wextra" "-Wno-unused-parameter" "-Wno-error=unknown-pragmas" "-Wuninitialized" "-Wno-missing-field-initializers" "-fno-strict-aliasing" "-Wno-error=deprecated" "-Wno-error=deprecated-declarations") list(APPEND ${C_FLAGS} "-Werror" "-Wstrict-prototypes") endif() endif() endmacro() macro(bc_apply_compile_flags SOURCE_FILES) if(${SOURCE_FILES}) set(options "") foreach(a ${ARGN}) if(${a}) string(REPLACE ";" " " options_${a} "${${a}}") set(options "${options} ${options_${a}}") endif() endforeach() if(options) set_source_files_properties(${${SOURCE_FILES}} PROPERTIES COMPILE_FLAGS "${options}") endif() endif() endmacro() macro(bc_git_version PROJECT_NAME PROJECT_VERSION) find_package(Git) add_custom_target(${PROJECT_NAME}-git-version COMMAND ${CMAKE_COMMAND} -DGIT_EXECUTABLE=${GIT_EXECUTABLE} -DPROJECT_NAME=${PROJECT_NAME} -DPROJECT_VERSION=${PROJECT_VERSION} -DWORK_DIR=${CMAKE_CURRENT_SOURCE_DIR} -DTEMPLATE_DIR=${BCTOOLBOX_CMAKE_UTILS_DIR} -DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${BCTOOLBOX_CMAKE_UTILS_DIR}/BcGitVersion.cmake BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/gitversion.h" ) endmacro() # TODO remove this macro macro(bc_project_build_version PROJECT_VERSION PROJECT_BUILD_VERSION) find_program (WC wc) if(WC) set(GIT_MINIMUM_VERSION 1.7.1) #might be even lower else() set(GIT_MINIMUM_VERSION 1.7.10) # --count option of git rev-list is available only since (more or less) git 1.7.10) endif() find_package(Git ${GIT_MINIMUM_VERSION}) string(COMPARE GREATER "${GIT_VERSION_STRING}" "1.7.10" GIT_REV_LIST_HAS_COUNT) if(GIT_REV_LIST_HAS_COUNT) set(GIT_REV_LIST_COMMAND "${GIT_EXECUTABLE}" "rev-list" "--count" "${PROJECT_VERSION}..HEAD") set(WC_COMMAND "more") #nop else() set(GIT_REV_LIST_COMMAND "${GIT_EXECUTABLE}" "rev-list" "${PROJECT_VERSION}..HEAD") set(WC_COMMAND "${WC}" "-l") endif() if(GIT_EXECUTABLE) execute_process( COMMAND ${GIT_REV_LIST_COMMAND} COMMAND ${WC_COMMAND} OUTPUT_VARIABLE PROJECT_VERSION_BUILD OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) endif() if (NOT PROJECT_VERSION_BUILD) set(PROJECT_VERSION_BUILD 0) endif() endmacro() macro(bc_generate_rpm_specfile SOURCE DEST) if(UNIX AND NOT APPLE AND NOT ANDROID) set(BC_PACKAGE_NAME_PREFIX "" CACHE STRING "Prefix for name of package.") get_cmake_property(_variableNames VARIABLES) set(RPM_ALL_CMAKE_OPTIONS "") foreach(_variableName ${_variableNames}) if(_variableName MATCHES "^ENABLE_") if(${_variableName}) set(${_variableName} 1) else() set(${_variableName} 0) endif() set(RPM_ALL_CMAKE_OPTIONS "${RPM_ALL_CMAKE_OPTIONS} -D${_variableName}=${${_variableName}}") endif() endforeach() configure_file(${SOURCE} ${DEST} @ONLY) unset(RPM_ALL_CMAKE_OPTIONS) unset(_variableNames) endif() endmacro() # Rules are following https://semver.org/ function(bc_compute_full_version OUTPUT_VERSION) find_program(GIT_EXECUTABLE git NAMES Git CMAKE_FIND_ROOT_PATH_BOTH) if(GIT_EXECUTABLE) execute_process( COMMAND "${GIT_EXECUTABLE}" "describe" OUTPUT_VARIABLE GIT_DESCRIBE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) # parse git describe version if (NOT (GIT_DESCRIBE_VERSION MATCHES "^([0-9]+)[.]([0-9]+)[.]([0-9]+)(-alpha|-beta)?(-[0-9]+)?(-g[0-9a-f]+)?$")) message(FATAL_ERROR "invalid git describe version: '${GIT_DESCRIBE_VERSION}'") endif() set(version_major ${CMAKE_MATCH_1}) set(version_minor ${CMAKE_MATCH_2}) set(version_patch ${CMAKE_MATCH_3}) if (CMAKE_MATCH_4) string(SUBSTRING "${CMAKE_MATCH_4}" 1 -1 version_prerelease) endif() if (CMAKE_MATCH_5) string(SUBSTRING "${CMAKE_MATCH_5}" 1 -1 version_commit) endif() if (CMAKE_MATCH_6) string(SUBSTRING "${CMAKE_MATCH_6}" 2 -1 version_hash) endif() # interpret untagged hotfixes as pre-releases of the next "patch" release if (NOT version_prerelease AND version_commit) math(EXPR version_patch "${version_patch} + 1") set(version_prerelease "pre") endif() # format full version set(full_version "${version_major}.${version_minor}.${version_patch}") if (version_prerelease) string(APPEND full_version "-${version_prerelease}") if (version_commit) string(APPEND full_version ".${version_commit}+${version_hash}") endif() endif() # check that the major and minor versions declared by the `project()` command are equal to the ones # that have been found out while parsing `git describe` result. if (PROJECT_VERSION) set(short_git_version "${version_major}.${version_minor}") set(short_project_version "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") if(NOT (short_project_version VERSION_EQUAL short_git_version)) message(FATAL_ERROR "project and git version are not compatible (project: '${PROJECT_VERSION}', git: '${full_version}', at: '${CMAKE_CURRENT_SOURCE_DIR}'): " "major and minor version are not equal !" ) endif() endif() set(${OUTPUT_VERSION} "${full_version}" PARENT_SCOPE) endif() endfunction() function(bc_compute_snapshots_or_releases_state OUTPUT_VERSION) find_program(GIT_EXECUTABLE git NAMES Git CMAKE_FIND_ROOT_PATH_BOTH) if(GIT_EXECUTABLE) execute_process( COMMAND "${GIT_EXECUTABLE}" "describe" OUTPUT_VARIABLE GIT_DESCRIBE_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) # Check git describe to see if we are on a release or not set(snapshots_or_releases_state "snapshots") if(NOT GIT_DESCRIBE_VERSION MATCHES ".*(alpha|beta).*") set(snapshots_or_releases_state "releases") endif() set(${OUTPUT_VERSION} "${snapshots_or_releases_state}" PARENT_SCOPE) endif() endfunction() # Allows to get the distinct parts of a full version number. # Parameters: # * version [input] : the full version number as string. # * major [output] : the major version. # * minor [output] : the minor version. # * patch [output] : the patch version. # * branch [output,optional] : aggregation of the branch name (alpha, beta, etc.) and the commit increment since the last tag. # The whole is prefixed by '-' and the result is empty for releases. # * hash [output,optional] : the commit hash prefixed by '+' or empty for releases. # # Example: # '5.2.1-alpha.145+e62e126' -> major : 5 # minor : 2 # patch : 1 # branch : '-alpha.145' # hash : '+e62e126' # # '5.2.1' -> major : 5 # minor : 2 # patch : 1 # branch : # hash : function(bc_parse_full_version version major minor patch) if ("${version}" MATCHES "^(0|[1-9][0-9]*)[.](0|[1-9][0-9]*)[.](0|[1-9][0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$") set(${major} "${CMAKE_MATCH_1}" PARENT_SCOPE) set(${minor} "${CMAKE_MATCH_2}" PARENT_SCOPE) set(${patch} "${CMAKE_MATCH_3}" PARENT_SCOPE) if (ARGC GREATER 4) set(${ARGV4} "${CMAKE_MATCH_4}" PARENT_SCOPE) endif() if (ARGC GREATER 5) set(${ARGV5} "${CMAKE_MATCH_5}" PARENT_SCOPE) endif() else() message(FATAL_ERROR "invalid full version '${version}'") endif() endfunction() # Translate the full semantic version into version numbers suitable for GNU/Linux packages (RPM/DEB) # Parameters: # * full_version_in [input] : the full version which has been computed by bc_compute_full_version(). # * package_version_out [output] : the name of a variable where to store the resulting package version number. # * package_release_out [output] : the name of a variable where to store the resulting package release number. # # Exemple: # '5.2.1-alpha.145+e62e126' -> version : '5.2.1' # release : '0.alpha.145+e62e126' # # '5.2.1' -> version : '5.2.1' # release : '1' function(bc_compute_linux_package_version full_version_in package_version_out package_release_out) bc_parse_full_version("${full_version_in}" version_major version_minor version_patch identifiers metadata) set(${package_version_out} "${version_major}.${version_minor}.${version_patch}" PARENT_SCOPE) if (NOT identifiers) set(${package_release_out} 1 PARENT_SCOPE) else() string(SUBSTRING "${identifiers}" 1 -1 identifiers) set(${package_release_out} "0.${identifiers}${metadata}" PARENT_SCOPE) endif() endfunction() function(bc_make_package_source_target) set(basename "") string(TOLOWER "${CMAKE_PROJECT_NAME}" basename) if (DEFINED BC_SPECFILE_NAME) set(specfile_name "${BC_SPECFILE_NAME}") else() set(specfile_name "${basename}.spec") endif() set(specfile_target "${basename}-rpm-spec") bc_generate_rpm_specfile("rpm/${specfile_name}.cmake" "rpm/${specfile_name}.cmake") add_custom_target(${specfile_target} COMMAND ${CMAKE_COMMAND} "-DPROJECT_VERSION=${PROJECT_VERSION}" "-DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}" "-DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR}" "-DBCTOOLBOX_CMAKE_UTILS=${BCTOOLBOX_CMAKE_UTILS}" "-DSRC=${CMAKE_CURRENT_BINARY_DIR}/rpm/${specfile_name}.cmake" "-DDEST=${PROJECT_SOURCE_DIR}/${specfile_name}" -P "${BCTOOLBOX_CMAKE_DIR}/ConfigureSpecfile.cmake" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} BYPRODUCTS "${PROJECT_SOURCE_DIR}/${specfile_name}" ) add_custom_target(package_source COMMAND ${CMAKE_COMMAND} "-DBCTOOLBOX_CMAKE_UTILS=${BCTOOLBOX_CMAKE_UTILS}" "-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}" "-DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}" "-DPROJECT_VERSION=${PROJECT_VERSION}" "-DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}" "-DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR}" "-DCPACK_PACKAGE_NAME=${CPACK_PACKAGE_NAME}" "-DEXCLUDE_PATTERNS='${CPACK_SOURCE_IGNORE_FILES}'" -P "${BCTOOLBOX_CMAKE_DIR}/MakeArchive.cmake" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPENDS ${specfile_target} ) endfunction() #Make a RELEASE file to work along liblinphone check_versions. It will be inside CMAKE_INSTALL_PREFIX but it will not be part of installation to avoid unwanted deployment. #OUTPUT_FOLDER is an out var and will be set as the folder in which the RELEASE file should be for check_version working. function(bc_get_release_file_folder OUTPUT_FOLDER) if(WIN32) set(${OUTPUT_FOLDER} "windows" PARENT_SCOPE) elseif(IOS) set(${OUTPUT_FOLDER} "ios" PARENT_SCOPE) elseif(ANDROID) set(${OUTPUT_FOLDER} "android" PARENT_SCOPE) elseif(APPLE) set(${OUTPUT_FOLDER} "macosx" PARENT_SCOPE) else() set(${OUTPUT_FOLDER} "linux" PARENT_SCOPE) endif() endfunction() function(bc_make_release_file full_version file_url) bc_parse_full_version(${full_version} LINPHONE_MAJOR_VERSION LINPHONE_MINOR_VERSION LINPHONE_MICRO_VERSION LINPHONE_BRANCH_VERSION) file(WRITE "${CMAKE_INSTALL_PREFIX}/RELEASE" "${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}.${LINPHONE_MICRO_VERSION}${LINPHONE_BRANCH_VERSION}\t${file_url}") endfunction() # This macro exactly behaves as CMake find_package() except it doesn't try to find the # package if a target with the same name already exists. Otherwise, find_package() # is executed with the same arguments than bc_find_package(). # Should the target exist, the following variable (where is string passed as # fist argument) are set in the current scope: # * _FOUND: systematically set to ON; # * _TARGETNAME: systematically set to . macro(bc_find_package name) if(TARGET ${name}) string(TOUPPER ${name} NAME) set(${name}_FOUND ON) set(${NAME}_FOUND ON) set(${name}_TARGETNAME ${name}) set(${NAME}_TARGETNAME ${name}) else() find_package(${ARGV}) endif() endmacro() ############################################################## bctoolbox-5.2.0/cmake/BcToolboxConfig.cmake.in000066400000000000000000000073701434566643100212440ustar00rootroot00000000000000############################################################################ # BcToolboxConfig.cmake # 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. # ############################################################################ # # Config file for the bctoolbox package. # Some components can be asked for: core, tester. # # It defines the following variables: # # BCTOOLBOX_FOUND - system has bctoolbox # BCTOOLBOX_INCLUDE_DIRS - the bctoolbox include directory # BCTOOLBOX_LIBRARIES - The libraries needed to use bctoolbox # BCTOOLBOX_CPPFLAGS - The compilation flags needed to use bctoolbox # BCTOOLBOX_LDFLAGS - The linking flags needed to use bctoolbox # BCTOOLBOX_${comp}_FOUND - system has bctoolbox "comp" component # BCTOOLBOX_${comp}_INCLUDE_DIRS - the bctoolbox "comp" component include directory # BCTOOLBOX_${comp}_LIBRARIES - The libraries needed to use bctoolbox "comp" component @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/bctoolboxTargets.cmake") list(APPEND bctoolbox_FIND_COMPONENTS core) if (ENABLE_TESTS_COMPONENT) list(APPEND bctoolbox_FIND_COMPONENTS tester) endif() list(REMOVE_DUPLICATES bctoolbox_FIND_COMPONENTS) set(bctoolbox_FIND_REQUIRED_core TRUE) set(BCTOOLBOX_CPPFLAGS @BCTOOLBOX_CPPFLAGS@) set(BCTOOLBOX_LDFLAGS "@LINK_FLAGS_STR@") set(BCTOOLBOX_LIBRARIES ) foreach(comp ${bctoolbox_FIND_COMPONENTS}) string(TOUPPER ${comp} uppercomp) if(comp STREQUAL "core") set(targetname bctoolbox) else() set(targetname bctoolbox-${comp}) endif() if(@ENABLE_SHARED@) set(BCTOOLBOX_${uppercomp}_LIBRARIES ${targetname}) else() set(targetname ${targetname}-static) if(TARGET ${targetname}) if(LINPHONE_BUILDER_GROUP_EXTERNAL_SOURCE_PATH_BUILDERS) set(BCTOOLBOX_${uppercomp}_LIBRARIES ${targetname}) else() get_target_property(BCTOOLBOX_${uppercomp}_LIBRARIES ${targetname} LOCATION) endif() if(NOT BCTOOLBOX_${uppercomp}_LIBRARIES) set(BCTOOLBOX_${uppercomp}_LIBRARIES) endif() get_target_property(BCTOOLBOX_${uppercomp}_LINK_LIBRARIES ${targetname} INTERFACE_LINK_LIBRARIES) if(BCTOOLBOX_${uppercomp}_LINK_LIBRARIES) list(APPEND BCTOOLBOX_${uppercomp}_LIBRARIES ${BCTOOLBOX_${uppercomp}_LINK_LIBRARIES}) endif() endif() endif() if(TARGET ${targetname}) get_target_property(BCTOOLBOX_${uppercomp}_INCLUDE_DIRS ${targetname} INTERFACE_INCLUDE_DIRECTORIES) if(NOT BCTOOLBOX_${uppercomp}_INCLUDE_DIRS) set(BCTOOLBOX_${uppercomp}_INCLUDE_DIRS) endif() list(APPEND BCTOOLBOX_LIBRARIES ${BCTOOLBOX_${uppercomp}_LIBRARIES}) list(APPEND BCTOOLBOX_INCLUDE_DIRS ${BCTOOLBOX_${uppercomp}_INCLUDE_DIRS}) set(BCTOOLBOX_${uppercomp}_FOUND 1) if(comp STREQUAL "core") set(BCTOOLBOX_FOUND 1) endif() elseif(bctoolbox_FIND_REQUIRED_${comp}) message(FATAL_ERROR "Required bctoolbox component \"${comp}\" cannot be found") endif() endforeach() set(BCTOOLBOX_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}") set(BCTOOLBOX_CMAKE_UTILS "${CMAKE_CURRENT_LIST_DIR}/bctoolboxCMakeUtils.cmake") include("${BCTOOLBOX_CMAKE_UTILS}") bctoolbox-5.2.0/cmake/ConfigureSpecfile.cmake000066400000000000000000000030731434566643100212060ustar00rootroot00000000000000############################################################################ # ConfigureSpecfile.cmake # 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. # ############################################################################ include("${BCTOOLBOX_CMAKE_UTILS}") set(FULL_VERSION ) bc_compute_full_version(FULL_VERSION) set(version_major ) set(version_minor ) set(version_patch ) set(identifiers ) set(metadata ) bc_parse_full_version("${FULL_VERSION}" version_major version_minor version_patch identifiers metadata) set(RPM_VERSION ${version_major}.${version_minor}.${version_patch}) if (NOT identifiers) set(RPM_RELEASE 1) else() string(SUBSTRING "${identifiers}" 1 -1 identifiers) set(RPM_RELEASE "0.${identifiers}${metadata}") endif() configure_file("${SRC}" "${DEST}") bctoolbox-5.2.0/cmake/FindDecaf.cmake000066400000000000000000000031041434566643100174100ustar00rootroot00000000000000############################################################################ # FindDecaf.txt # Copyright (C) 2017 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 decaf include files and library # # DECAF_FOUND - system has lib decaf # DECAF_INCLUDE_DIRS - the decaf include directory # DECAF_LIBRARY - The library needed to use decaf include(CMakePushCheckState) include(CheckIncludeFile) find_path(DECAF_INCLUDE_DIRS NAMES decaf.h PATH_SUFFIXES include/decaf ) # find the three mbedtls library find_library(DECAF_LIBRARY NAMES decaf ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Decaf DEFAULT_MSG DECAF_INCLUDE_DIRS DECAF_LIBRARY ) mark_as_advanced(DECAF_INCLUDE_DIRS DECAF_LIBRARY) bctoolbox-5.2.0/cmake/FindIconv.cmake000066400000000000000000000034311434566643100174670ustar00rootroot00000000000000############################################################################ # FindIconv.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 iconv include file and library # # ICONV_FOUND - system has libiconv # ICONV_INCLUDE_DIRS - the libiconv include directory # ICONV_LIBRARIES - The libraries needed to use libiconv if(APPLE AND NOT IOS) set(ICONV_HINTS "${CMAKE_OSX_SYSROOT}/usr" "/usr") endif() if(ICONV_HINTS) set(ICONV_LIBRARIES_HINTS "${ICONV_HINTS}/lib") endif() find_path(ICONV_INCLUDE_DIRS NAMES iconv.h HINTS "${ICONV_HINTS}" PATH_SUFFIXES include ) if(ICONV_INCLUDE_DIRS) set(HAVE_ICONV_H 1) endif() find_library(ICONV_LIBRARIES NAMES iconv HINTS "${ICONV_LIBRARIES_HINTS}" ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Iconv DEFAULT_MSG ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H ) mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES HAVE_ICONV_H) bctoolbox-5.2.0/cmake/FindMbedTLS.cmake000066400000000000000000000064471434566643100176550ustar00rootroot00000000000000############################################################################ # FindMdebTLS.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 mbedTLS include file and library # # MBEDTLS_FOUND - system has mbedTLS # MBEDTLS_INCLUDE_DIRS - the mbedTLS include directory # MBEDTLS_LIBRARIES - The libraries needed to use mbedTLS include(CMakePushCheckState) include(CheckIncludeFile) include(CheckCSourceCompiles) include(CheckSymbolExists) find_path(MBEDTLS_INCLUDE_DIRS NAMES mbedtls/ssl.h PATH_SUFFIXES include ) # find the three mbedtls library find_library(MBEDTLS_LIBRARY NAMES mbedtls ) find_library(MBEDX509_LIBRARY NAMES mbedx509 ) find_library(MBEDCRYPTO_LIBRARY NAMES mbedcrypto ) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIRS} ${CMAKE_REQUIRED_INCLUDES_${BUILD_TYPE}}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) # check we have a mbedTLS version 2 or above(all functions are prefixed mbedtls_) if(MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY) check_symbol_exists(mbedtls_ssl_init "mbedtls/ssl.h" MBEDTLS_V2) if(NOT MBEDTLS_V2) message ("MESSAGE: NO MBEDTLS_V2") message ("MESSAGE: MBEDTLS_LIBRARY=" ${MBEDTLS_LIBRARY}) message ("MESSAGE: MBEDX509_LIBRARY=" ${MBEDX509_LIBRARY}) message ("MESSAGE: MBEDCRYPTO_LIBRARY=" ${MBEDCRYPTO_LIBRARY}) endif() endif() check_symbol_exists(mbedtls_ssl_conf_dtls_srtp_protection_profiles "mbedtls/ssl.h" DTLS_SRTP_AVAILABLE) # Define the imported target for the three mbedtls libraries foreach(targetname "mbedtls" "mbedx509" "mbedcrypto") string(TOUPPER ${targetname} varprefix) add_library(${targetname} SHARED IMPORTED) if (WIN32) set_target_properties(${targetname} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${varprefix}_INCLUDE_DIRS}" IMPORTED_IMPLIB "${${varprefix}_LIBRARY}" ) else() set_target_properties(${targetname} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${varprefix}_INCLUDE_DIRS}" IMPORTED_LOCATION "${${varprefix}_LIBRARY}" ) endif() endforeach() unset(varprefix) # MBEDTLS_LIBRARIES only needs to contain the name of the targets set (MBEDTLS_LIBRARIES mbedtls mbedx509 mbedcrypto ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES ) cmake_pop_check_state() mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES DTLS_SRTP_AVAILABLE) bctoolbox-5.2.0/cmake/FindPolarSSL.cmake000066400000000000000000000045111434566643100200500ustar00rootroot00000000000000############################################################################ # FindPolarSSL.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 polarssl include file and library # # POLARSSL_FOUND - system has polarssl # POLARSSL_INCLUDE_DIRS - the polarssl include directory # POLARSSL_LIBRARIES - The libraries needed to use polarssl include(CMakePushCheckState) include(CheckIncludeFile) include(CheckCSourceCompiles) include(CheckSymbolExists) find_path(POLARSSL_INCLUDE_DIRS NAMES polarssl/ssl.h PATH_SUFFIXES include ) if(POLARSSL_INCLUDE_DIRS) set(HAVE_POLARSSL_SSL_H 1) endif() find_library(POLARSSL_LIBRARIES NAMES polarssl mbedtls PATH_SUFFIXES bin lib ) if(POLARSSL_LIBRARIES) #x509parse_crtpath is present in polarssl1.3 but not 1.2, use it to check what version is present cmake_push_check_state(RESET) set(CMAKE_REQUIRED_INCLUDES ${POLARSSL_INCLUDE_DIRS}) set(CMAKE_REQUIRED_LIBRARIES ${POLARSSL_LIBRARIES}) check_symbol_exists(ssl_get_dtls_srtp_protection_profile "polarssl/ssl.h" HAVE_SSL_GET_DTLS_SRTP_PROTECTION_PROFILE) check_symbol_exists(ctr_drbg_free "polarssl/ctr_drbg.h" CTR_DRBG_FREE) cmake_pop_check_state() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PolarSSL DEFAULT_MSG POLARSSL_INCLUDE_DIRS POLARSSL_LIBRARIES HAVE_POLARSSL_SSL_H ) mark_as_advanced(POLARSSL_INCLUDE_DIRS POLARSSL_LIBRARIES HAVE_POLARSSL_SSL_H POLARSSL_VERSION13_OK CTR_DRGB_FREE HAVE_SSL_GET_DTLS_SRTP_PROTECTION_PROFILE) bctoolbox-5.2.0/cmake/MakeArchive.cmake000066400000000000000000000027441434566643100177750ustar00rootroot00000000000000############################################################################ # MakeArchive.cmake # 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. # ############################################################################ include("${BCTOOLBOX_CMAKE_UTILS}") bc_compute_full_version(version) set(archive_name "${CPACK_PACKAGE_NAME}-${version}") set(archive_path "${PROJECT_BINARY_DIR}/${archive_name}.tar.gz") find_program(TAR tar) set(EXCLUDE_ARGS ) foreach (pattern ${EXCLUDE_PATTERNS}) list(APPEND EXCLUDE_ARGS "--exclude=${pattern}") endforeach() execute_process(COMMAND ${TAR} -C "${PROJECT_SOURCE_DIR}" -cz -f "${archive_path}" "--transform" "s,^\\.,${archive_name}," ${EXCLUDE_ARGS} .) bctoolbox-5.2.0/cmake/gitversion.h.in000066400000000000000000000014641434566643100175610ustar00rootroot00000000000000/* linphone Copyright (C) 2010-2017 Belledonne Communications SARL 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. */ #define @PREFIX_GIT_VERSION@_GIT_VERSION "@PROJECT_GIT_VERSION@"bctoolbox-5.2.0/config.h.cmake000066400000000000000000000025761434566643100162340ustar00rootroot00000000000000/*************************************************************************** * config.h.cmake * Copyright (C) 2010-2022 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_DTLS_SRTP 1 #cmakedefine HAVE_DECAF 1 #cmakedefine HAVE_MBEDTLS 1 #cmakedefine HAVE_POLARSSL 1 #cmakedefine HAVE_CTR_DRGB_FREE 1 #cmakedefine HAVE_CU_GET_SUITE 1 #cmakedefine HAVE_CU_CURSES 1 #cmakedefine HAVE_CU_SET_TRACE_HANDLER 1 #cmakedefine ENABLE_DEFAULT_LOG_HANDLER 1 #cmakedefine HAVE_LIBRT 1 #cmakedefine BCTBX_STATIC #cmakedefine HAVE_EXECINFO bctoolbox-5.2.0/include/000077500000000000000000000000001434566643100151505ustar00rootroot00000000000000bctoolbox-5.2.0/include/CMakeLists.txt000066400000000000000000000035231434566643100177130ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2016 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(HEADER_FILES charconv.h compiler.h defs.h exception.hh utils.hh list.h logging.h map.h ownership.hh parser.h port.h regex.h vconnect.h vfs.h vfs_standard.h vfs_encrypted.hh param_string.h ) if(APPLE) list(APPEND HEADER_FILES ios_utils.hh) endif() if(MBEDTLS_FOUND OR POLARSSL_FOUND) list(APPEND HEADER_FILES crypto.h) list(APPEND HEADER_FILES crypto.hh) endif() if(ENABLE_TESTS_COMPONENT) list(APPEND HEADER_FILES tester.h) endif() set(BCTOOLBOX_HEADER_FILES ) foreach(HEADER_FILE ${HEADER_FILES}) list(APPEND BCTOOLBOX_HEADER_FILES "${CMAKE_CURRENT_LIST_DIR}/bctoolbox/${HEADER_FILE}") endforeach() set(BCTOOLBOX_HEADER_FILES ${BCTOOLBOX_HEADER_FILES} PARENT_SCOPE) install(FILES ${BCTOOLBOX_HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/bctoolbox PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) bctoolbox-5.2.0/include/bctoolbox/000077500000000000000000000000001434566643100171435ustar00rootroot00000000000000bctoolbox-5.2.0/include/bctoolbox/charconv.h000066400000000000000000000062571434566643100211310ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_CHARCONV_H #define BCTBX_CHARCONV_H #include "bctoolbox/port.h" #ifdef __cplusplus extern "C" { #endif /** * @brief Set the default encoding for the application. * * @param[in] encoding default encoding, "locale" to set it to the system's locale */ BCTBX_PUBLIC void bctbx_set_default_encoding (const char *encoding); /** * @brief Return the default encoding for the application. * * @return a pointer to a null-terminated string containing the default encoding. */ BCTBX_PUBLIC const char *bctbx_get_default_encoding (void); /** * @brief Convert the given string from system locale to UTF8. * * @param[in] str string to convert * * @return a pointer to a null-terminated string containing the converted string. This buffer must then be freed * by caller. NULL on failure. */ BCTBX_PUBLIC char *bctbx_locale_to_utf8 (const char *str); /** * @brief Convert the given string from UTF8 to system locale. * * @param[in] str string to convert * * @return a pointer to a null-terminated string containing the converted string. This buffer must then be freed * by caller. NULL on failure. */ BCTBX_PUBLIC char *bctbx_utf8_to_locale (const char *str); /** * @brief Convert the given string. * * @param[in] str string to convert * @param[in] encoding charset of the string * * @return a pointer to a null-terminated string containing the converted string. This buffer must then be freed * by caller. NULL on failure. * * @note If encoding is equal to "locale" then it will use the system's locale * @note If encoding is UTF-8 then it returns a copy of str */ BCTBX_PUBLIC char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding); /** * @brief Convert the char string to wide char string. Only available for Windows platform * * @param[in] str string to convert * * @return a pointer to a null-terminated string containing the converted string. This buffer must then be freed * by caller. NULL on failure. */ BCTBX_PUBLIC wchar_t* bctbx_string_to_wide_string(const char* s); /** * @brief Return the code page from the encoding. Only available for Windows platform * * @param[in] encoding string to convert. If NULL or "", return the code page defined by bctbx_get_default_encoding(). @maybenil * * @return The code page associated to the encoding. Return system's locale if not found. */ BCTBX_PUBLIC unsigned int bctbx_get_code_page(const char* encoding); #ifdef __cplusplus } #endif #endif /* BCTBX_CHARCONV_H */ bctoolbox-5.2.0/include/bctoolbox/compiler.h000066400000000000000000000031311434566643100211240ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 COMPILER_H #define COMPILER_H #ifdef __has_feature #if __has_feature(address_sanitizer) #define BCTBX_ASAN_ENABLED #endif // if __has_feature(address_sanitizer) #elif defined(__SANITIZE_ADDRESS__) #define BCTBX_ASAN_ENABLED #endif // ifdef __has_feature #ifdef BCTBX_ASAN_ENABLED #define BCTBX_DISABLE_ASAN __attribute__((no_sanitize_address)) #else #define BCTBX_DISABLE_ASAN #endif // ifdef BCTBX_ASAN_ENABLED #ifdef __has_attribute #if __has_attribute(no_sanitize) #define BCTBX_DISABLE_UBSAN __attribute__((no_sanitize("undefined"))) #else #define BCTBX_DISABLE_UBSAN #endif // __has_attribute(no_sanitize) #elif defined(__GNUC__) && !defined(__MINGW32__) && GCC_VERSION >= 40900 #define BCTBX_DISABLE_UBSAN __attribute__((no_sanitize_undefined)) #else #define BCTBX_DISABLE_UBSAN #endif // ifdef __has_attribute #endif // ifdef COMPILER_H bctoolbox-5.2.0/include/bctoolbox/crypto.h000066400000000000000000001514371434566643100206470ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_CRYPTO_H #define BCTBX_CRYPTO_H #include "bctoolbox/port.h" #include "bctoolbox/list.h" /* key agreements settings defines */ /* Each algo is defined as a bit toggled in a 32 bits integer, * so we can easily ask for all availables ones */ #define BCTBX_DHM_UNSET 0x00000000 #define BCTBX_DHM_2048 0x00000001 #define BCTBX_DHM_3072 0x00000002 #define BCTBX_ECDH_X25519 0x00000004 #define BCTBX_ECDH_X448 0x00000008 #define BCTBX_KEM_KYBER512 0x00000010 #define BCTBX_KEM_KYBER768 0x00000020 #define BCTBX_KEM_KYBER1024 0x00000040 #define BCTBX_KEM_HQC128 0x00000080 #define BCTBX_KEM_HQC192 0x00000100 #define BCTBX_KEM_HQC256 0x00000200 #define BCTBX_KEM_X25519 0x00000400 #define BCTBX_KEM_X448 0x00000800 /* EdDSA defines */ #define BCTBX_EDDSA_UNSET 0 #define BCTBX_EDDSA_25519 1 #define BCTBX_EDDSA_448 2 #define BCTBX_VERIFY_SUCCESS 0 #define BCTBX_VERIFY_FAILED -1 /* Elliptic Curves Key lengths defines: theses are redefines of the values defined in decaf headers made available to bctoolbox library users bctoolbox will fail to compile if these values are not in sync with the decaf ones */ #define BCTBX_ECDH_X25519_PUBLIC_SIZE 32 #define BCTBX_ECDH_X25519_PRIVATE_SIZE BCTBX_ECDH_X25519_PUBLIC_SIZE #define BCTBX_ECDH_X448_PUBLIC_SIZE 56 #define BCTBX_ECDH_X448_PRIVATE_SIZE BCTBX_ECDH_X448_PUBLIC_SIZE #define BCTBX_EDDSA_25519_PUBLIC_SIZE 32 #define BCTBX_EDDSA_25519_PRIVATE_SIZE BCTBX_EDDSA_25519_PUBLIC_SIZE #define BCTBX_EDDSA_25519_SIGNATURE_SIZE (BCTBX_EDDSA_25519_PUBLIC_SIZE + BCTBX_EDDSA_25519_PRIVATE_SIZE) #define BCTBX_EDDSA_448_PUBLIC_SIZE 57 #define BCTBX_EDDSA_448_PRIVATE_SIZE BCTBX_EDDSA_448_PUBLIC_SIZE #define BCTBX_EDDSA_448_SIGNATURE_SIZE (BCTBX_EDDSA_448_PUBLIC_SIZE + BCTBX_EDDSA_448_PRIVATE_SIZE) /* SSL settings defines */ #define BCTBX_SSL_UNSET -1 #define BCTBX_SSL_IS_CLIENT 0 #define BCTBX_SSL_IS_SERVER 1 #define BCTBX_SSL_TRANSPORT_STREAM 0 #define BCTBX_SSL_TRANSPORT_DATAGRAM 1 #define BCTBX_SSL_VERIFY_NONE 0 #define BCTBX_SSL_VERIFY_OPTIONAL 1 #define BCTBX_SSL_VERIFY_REQUIRED 2 /* Encryption/decryption defines */ #define BCTBX_GCM_ENCRYPT 1 #define BCTBX_GCM_DECRYPT 0 /* Error codes : All error codes are negative and defined on 32 bits on format -0x7XXXXXXX * in order to be sure to not overlap on crypto librairy (polarssl or mbedtls for now) which are defined on 16 bits 0x[7-0]XXX */ #define BCTBX_ERROR_UNSPECIFIED_ERROR -0x70000000 #define BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL -0x70001000 #define BCTBX_ERROR_INVALID_BASE64_INPUT -0x70002000 #define BCTBX_ERROR_INVALID_INPUT_DATA -0x70004000 #define BCTBX_ERROR_UNAVAILABLE_FUNCTION -0x70008000 /* key related */ #define BCTBX_ERROR_UNABLE_TO_PARSE_KEY -0x70010000 /* Certificate related */ #define BCTBX_ERROR_INVALID_CERTIFICATE -0x70020000 #define BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL -0x70020001 #define BCTBX_ERROR_CERTIFICATE_WRITE_PEM -0x70020002 #define BCTBX_ERROR_CERTIFICATE_PARSE_PEM -0x70020004 #define BCTBX_ERROR_UNSUPPORTED_HASH_FUNCTION -0x70020008 /* SSL related */ #define BCTBX_ERROR_INVALID_SSL_CONFIG -0x70030001 #define BCTBX_ERROR_INVALID_SSL_TRANSPORT -0x70030002 #define BCTBX_ERROR_INVALID_SSL_ENDPOINT -0x70030004 #define BCTBX_ERROR_INVALID_SSL_AUTHMODE -0x70030008 #define BCTBX_ERROR_INVALID_SSL_CONTEXT -0x70030010 #define BCTBX_ERROR_NET_WANT_READ -0x70032000 #define BCTBX_ERROR_NET_WANT_WRITE -0x70034000 #define BCTBX_ERROR_SSL_PEER_CLOSE_NOTIFY -0x70038000 #define BCTBX_ERROR_NET_CONN_RESET -0x70030000 /* Symmetric ciphers related */ #define BCTBX_ERROR_AUTHENTICATION_FAILED -0x70040000 /* certificate verification flags codes */ #define BCTBX_CERTIFICATE_VERIFY_ALL_FLAGS 0xFFFFFFFF #define BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED 0x01 /**< The certificate validity has expired. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED 0x02 /**< The certificate has been revoked (is on a CRL). */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH 0x04 /**< The certificate Common Name (CN) does not match with the expected CN. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED 0x08 /**< The certificate is not correctly signed by the trusted CA. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING 0x10 /**< Certificate was missing. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_SKIP_VERIFY 0x20 /**< Certificate verification was skipped. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_OTHER 0x0100 /**< Other reason (can be used by verify callback) */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_FUTURE 0x0200 /**< The certificate validity starts in the future. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_KEY_USAGE 0x0400 /**< Usage does not match the keyUsage extension. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_EXT_KEY_USAGE 0x0800 /**< Usage does not match the extendedKeyUsage extension. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_NS_CERT_TYPE 0x1000 /**< Usage does not match the nsCertType extension. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_MD 0x2000 /**< The certificate is signed with an unacceptable hash. */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_PK 0x4000 /**< The certificate is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ #define BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_KEY 0x8000 /**< The certificate is signed with an unacceptable key (eg bad curve, RSA too short). */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_FUTURE 0x10000 /**< The CRL is from the future */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED 0x20000 /**< CRL is not correctly signed by the trusted CA. */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED 0x40000 /**< CRL is expired. */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_MD 0x80000 /**< The CRL is signed with an unacceptable hash. */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_PK 0x100000 /**< The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ #define BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_KEY 0x200000 /**< The CRL is signed with an unacceptable key (eg bad curve, RSA too short). */ /* Hash functions type */ typedef enum bctbx_md_type { BCTBX_MD_UNDEFINED, BCTBX_MD_SHA1, BCTBX_MD_SHA224, BCTBX_MD_SHA256, BCTBX_MD_SHA384, BCTBX_MD_SHA512} bctbx_md_type_t; /* Dtls srtp protection profile */ typedef enum bctbx_srtp_profile { BCTBX_SRTP_UNDEFINED, BCTBX_SRTP_AES128_CM_HMAC_SHA1_80, BCTBX_SRTP_AES128_CM_HMAC_SHA1_32, BCTBX_SRTP_NULL_HMAC_SHA1_80, BCTBX_SRTP_NULL_HMAC_SHA1_32 } bctbx_dtls_srtp_profile_t; typedef enum bctbx_type_implementation { BCTBX_POLARSSL, BCTBX_POLARSSL1_2, BCTBX_MBEDTLS } bctbx_type_implementation_t; #ifdef __cplusplus extern "C"{ #endif /*****************************************************************************/ /****** Utils ******/ /*****************************************************************************/ /** * @brief Return a string translation of an error code * PolarSSL and mbedTLS error codes are on 16 bits always negatives, and these are forwarded to the crypto library error to string translation * Specific bctoolbox error code are on 32 bits, all in the form -0x7XXX XXXX * Output string is truncated if the buffer is too small and always include a null termination char * * @param[in] error_code The error code * @param[in/out] buffer Buffer to place error string representation * @param[in] buffer_length Size of the buffer in bytes. */ BCTBX_PUBLIC void bctbx_strerror(int32_t error_code, char *buffer, size_t buffer_length); /** * @brief Encode a buffer into base64 format * @param[out] output base64 encoded buffer * @param[in/out] output_length output buffer max size and actual size of buffer after encoding * @param[in] input source plain buffer * @param[in] input_length Length in bytes of plain buffer to be encoded * * @return 0 if success or BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL if the output buffer cannot contain the encoded data * * @note If the function is called with *output_length=0, set the requested buffer size in output_length */ BCTBX_PUBLIC int32_t bctbx_base64_encode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length); /** * @brief Decode a base64 formatted buffer. * @param[out] output plain buffer * @param[in/out] output_length output buffer max size and actual size of buffer after decoding * @param[in] input source base64 encoded buffer * @param[in] input_length Length in bytes of base64 buffer to be decoded * * @return 0 if success, BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL if the output buffer cannot contain the decoded data * or BCTBX_ERROR_INVALID_BASE64_INPUT if encoded buffer was incorrect base64 data * * @note If the function is called with *output_length=0, set the requested buffer size in output_length */ BCTBX_PUBLIC int32_t bctbx_base64_decode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length); /*****************************************************************************/ /****** Random Number Generation ******/ /*****************************************************************************/ /** @brief An opaque structure used to store RNG context * Instanciate pointers only and allocate them using the bctbx_rng_context_new() function */ typedef struct bctbx_rng_context_struct bctbx_rng_context_t; /** * @brief Create and initialise the Random Number Generator context * @return a pointer to the RNG context */ BCTBX_PUBLIC bctbx_rng_context_t *bctbx_rng_context_new(void); /** * @brief Get some random material * * @param[in/out] context The RNG context to be used * @param[out] output A destination buffer for the random material generated * @param[in] output_length Size in bytes of the output buffer and requested random material * * @return 0 on success */ BCTBX_PUBLIC int32_t bctbx_rng_get(bctbx_rng_context_t *context, unsigned char*output, size_t output_length); /** * @brief Clear the RNG context and free internal buffer * * @param[in] context The RNG context to clear */ BCTBX_PUBLIC void bctbx_rng_context_free(bctbx_rng_context_t *context); /*****************************************************************************/ /***** Signing key *****/ /*****************************************************************************/ /** @brief An opaque structure used to store the signing key context * Instanciate pointers only and allocate them using the bctbx_signing_key_new() function */ typedef struct bctbx_signing_key_struct bctbx_signing_key_t; /** * @brief Create and initialise a signing key context * @return a pointer to the signing key context */ BCTBX_PUBLIC bctbx_signing_key_t *bctbx_signing_key_new(void); /** * @brief Clear the signing key context and free internal buffer * * @param[in] key The signing key context to clear */ BCTBX_PUBLIC void bctbx_signing_key_free(bctbx_signing_key_t *key); /** * @brief Write the key in a buffer as a PEM string * * @param[in] key The signing key to be extracted in PEM format * * @return a pointer to a null terminated string containing the key in PEM format. This buffer must then be freed by caller. NULL on failure. */ BCTBX_PUBLIC char *bctbx_signing_key_get_pem(bctbx_signing_key_t *key); /** * @brief Parse signing key in PEM format from a null terminated string buffer * * @param[in/out] key An already initialised signing key context * @param[in] buffer The input buffer containing a PEM format key in a null terminated string * @param[in] buffer_length The length of input buffer, including the NULL termination char * @param[in] password Password for decryption(may be NULL) * @param[in] passzord_length size of password * * @return 0 on success */ BCTBX_PUBLIC int32_t bctbx_signing_key_parse(bctbx_signing_key_t *key, const char *buffer, size_t buffer_length, const unsigned char *password, size_t password_length); /** * @brief Parse signing key from a file * * @param[in/out] key An already initialised signing key context * @param[in] path filename to read the key from * @param[in] password Password for decryption(may be NULL) * * @return 0 on success */ BCTBX_PUBLIC int32_t bctbx_signing_key_parse_file(bctbx_signing_key_t *key, const char *path, const char *password); /*****************************************************************************/ /***** X509 Certificate *****/ /*****************************************************************************/ /** @brief An opaque structure used to store the certificate context * Instanciate pointers only and allocate them using the bctbx_x509_certificate_new() function */ typedef struct bctbx_x509_certificate_struct bctbx_x509_certificate_t; /** * @brief Create and initialise a x509 certificate context * @return a pointer to the certificate context */ BCTBX_PUBLIC bctbx_x509_certificate_t *bctbx_x509_certificate_new(void); /** * @brief Clear the certificate context and free internal buffer * * @param[in] cert The x509 certificate context to clear */ BCTBX_PUBLIC void bctbx_x509_certificate_free(bctbx_x509_certificate_t *cert); /** * @brief Write the certificate in a buffer as a PEM string * * @param[in] cert The certificate to be extracted in PEM format * * @return a pointer to a null terminated string containing the certificate in PEM format. This buffer must then be freed by caller. NULL on failure. */ BCTBX_PUBLIC char *bctbx_x509_certificates_chain_get_pem(const bctbx_x509_certificate_t *cert); /** * @brief Return an informational string about the certificate * * @param[out] buf Buffer to receive the output * @param[in] size Maximum output buffer size * @param[in] prefix A line prefix * @param[in] cert The x509 certificate * * @return The length of the string written or a negative error code */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_info_string(char *buf, size_t size, const char *prefix, const bctbx_x509_certificate_t *cert); /** * @brief Parse an x509 certificate in PEM format from a null terminated string buffer * * @param[in/out] cert An already initialised x509 certificate context * @param[in] buffer The input buffer containing a PEM format certificate in a null terminated string * @param[in] buffer_length The length of input buffer, including the NULL termination char * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_parse(bctbx_x509_certificate_t *cert, const char *buffer, size_t buffer_length); /** * @brief Load one or more certificates and add them to the chained list * * @param[in/out] cert points to the start of the chain, can be an empty initialised certificate context * @param[in] path filename to read the certificate from * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_parse_file(bctbx_x509_certificate_t *cert, const char *path); /** * @brief Load one or more certificates files from a path and add them to the chained list * * @param[in/out] cert points to the start of the chain, can be an empty initialised certificate context * @param[in] path directory to read certicates files from * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_parse_path(bctbx_x509_certificate_t *cert, const char *path); /** * @brief Get the length in bytes of a certifcate chain in DER format * * @param[in] cert The certificate chain * * @return The length in bytes of the certificate buffer in DER format, 0 if no certificate found */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_der_length(bctbx_x509_certificate_t *cert); /** * @brief Get the certificate in DER format in a null terminated string * * @param[in] cert The certificate chain * @param[in/out] buffer The buffer to hold the certificate * @param[in] buffer_length Maximum output buffer size * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_der(bctbx_x509_certificate_t *cert, unsigned char *buffer, size_t buffer_length); /** * @brief Store the certificate subject DN in printable form into buf * * @param[in] cert The x509 certificate * @param[in/out] dn A buffer to store the DN string * @param[in] dn_length Maximum size to be written in buffer * * @return The length of the string written (not including the terminated nul byte), or a negative error code */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_subject_dn(const bctbx_x509_certificate_t *cert, char *dn, size_t dn_length); /** * @brief Obtain the certificate subjects (all subjectAltName URIS and DNS + subject CN) * * @param[in] cert The x509 certificate * @return a list of allocated strings (char*), to be freed with bctbx_free() */ BCTBX_PUBLIC bctbx_list_t *bctbx_x509_certificate_get_subjects(const bctbx_x509_certificate_t *cert); /** * @brief Generate certificate fingerprint (hash of the DER format certificate) hexadecimal format in a null terminated string * * @param[in] cert The x509 certificate * @param[in/out] fingerprint The buffer to hold the fingerprint(null terminated string in hexadecimal) * @param[in] fingerprint_length Maximum length of the fingerprint buffer * @param[in] hash_algorithm set to BCTBX_MD_UNDEFINED to use the hash used in certificate signature(recommended) * or specify an other hash algorithm(BCTBX_MD_SHA1, BCTBX_MD_SHA224, BCTBX_MD_SHA256, BCTBX_MD_SHA384, BCTBX_MD_SHA512) * @return length of written on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_fingerprint(const bctbx_x509_certificate_t *cert, char *fingerprint, size_t fingerprint_length, bctbx_md_type_t hash_algorithm); /** * @brief Retrieve the certificate signature hash function * * @param[in] cert The x509 certificate * @param[out] hash_algorithm The hash algorithm used for the certificate signature or BCTBX_MD_UNDEFINED if unable to retrieve it * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_get_signature_hash_function(const bctbx_x509_certificate_t *certificate, bctbx_md_type_t *hash_algorithm); /** * @brief Generate a self-signed certificate using RSA 3072 bits signature algorithm * * @param[in] subject The certificate subject * @param[in/out] certificate An empty intialised certificate pointer to hold the generated certificate * @param[in/out] pkey An empty initialised signing key pointer to hold the key generated and used to sign the certificate (RSA 3072 bits) * @param[out] pem If not null, a buffer to hold a PEM string of the certificate and key * @param[in] pem_length pem buffer length * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_generate_selfsigned(const char *subject, bctbx_x509_certificate_t *certificate, bctbx_signing_key_t *pkey, char *pem, size_t pem_length); /** * @brief Convert underlying crypto library certificate flags into a printable string * * @param[out] buffer a buffer to hold the output string * @param[in] buffer_size maximum buffer size * @param[in] flags The flags from the underlying crypto library, provided in callback functions * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_flags_to_string(char *buffer, size_t buffer_size, uint32_t flags); /** * @brief Set a certificate flags (using underlying crypto library defines) * * @param[in/out] flags The certificate flags holder directly provided by crypto library in a callback function * @param[in] flags_to_set Flags to be set, bctoolbox defines * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_set_flag(uint32_t *flags, uint32_t flags_to_set); /** * @brief convert certificate flags from underlying crypto library defines to bctoolbox ones * * @param[in] flags certificate flags provided by the crypto library in a callback function * * @return same flag but using the bctoolbox API definitions */ BCTBX_PUBLIC uint32_t bctbx_x509_certificate_remap_flag(uint32_t flags); /** * @brief Unset a certificate flags (using underlying crypto library defines) * * @param[in/out] flags The certificate flags holder directly provided by crypto library in a callback function * @param[in] flags_to_set Flags to be unset, bctoolbox defines * * @return 0 on success, negative error code otherwise */ BCTBX_PUBLIC int32_t bctbx_x509_certificate_unset_flag(uint32_t *flags, uint32_t flags_to_unset); /*****************************************************************************/ /***** SSL *****/ /*****************************************************************************/ typedef struct bctbx_ssl_context_struct bctbx_ssl_context_t; typedef struct bctbx_ssl_config_struct bctbx_ssl_config_t; BCTBX_PUBLIC bctbx_type_implementation_t bctbx_ssl_get_implementation_type(void); BCTBX_PUBLIC bctbx_ssl_context_t *bctbx_ssl_context_new(void); BCTBX_PUBLIC void bctbx_ssl_context_free(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int32_t bctbx_ssl_context_setup(bctbx_ssl_context_t *ssl_ctx, bctbx_ssl_config_t *ssl_config); BCTBX_PUBLIC int32_t bctbx_ssl_close_notify(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int32_t bctbx_ssl_session_reset(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int32_t bctbx_ssl_read(bctbx_ssl_context_t *ssl_ctx, unsigned char *buf, size_t buf_length); BCTBX_PUBLIC int32_t bctbx_ssl_write(bctbx_ssl_context_t *ssl_ctx, const unsigned char *buf, size_t buf_length); BCTBX_PUBLIC int32_t bctbx_ssl_set_hostname(bctbx_ssl_context_t *ssl_ctx, const char *hostname); BCTBX_PUBLIC int32_t bctbx_ssl_handshake(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int32_t bctbx_ssl_set_hs_own_cert(bctbx_ssl_context_t *ssl_ctx, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key); BCTBX_PUBLIC void bctbx_ssl_set_io_callbacks(bctbx_ssl_context_t *ssl_ctx, void *callback_data, int(*callback_send_function)(void *, const unsigned char *, size_t), /* callbacks args are: callback data, data buffer to be send, size of data buffer */ int(*callback_recv_function)(void *, unsigned char *, size_t)); /* args: callback data, data buffer to be read, size of data buffer */ BCTBX_PUBLIC const bctbx_x509_certificate_t *bctbx_ssl_get_peer_certificate(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC const char *bctbx_ssl_get_ciphersuite(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int bctbx_ssl_get_ciphersuite_id(const char* ciphersuite); BCTBX_PUBLIC const char *bctbx_ssl_get_version(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC bctbx_ssl_config_t *bctbx_ssl_config_new(void); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_crypto_library_config(bctbx_ssl_config_t *ssl_config, void *internal_config); BCTBX_PUBLIC void bctbx_ssl_config_free(bctbx_ssl_config_t *ssl_config); BCTBX_PUBLIC void *bctbx_ssl_config_get_private_config(bctbx_ssl_config_t *ssl_config); BCTBX_PUBLIC int32_t bctbx_ssl_config_defaults(bctbx_ssl_config_t *ssl_config, int endpoint, int transport); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_endpoint(bctbx_ssl_config_t *ssl_config, int endpoint); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_transport (bctbx_ssl_config_t *ssl_config, int transport); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_authmode(bctbx_ssl_config_t *ssl_config, int authmode); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_rng(bctbx_ssl_config_t *ssl_config, int(*rng_function)(void *, unsigned char *, size_t), void *rng_context); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_callback_verify(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_x509_certificate_t *, int, uint32_t *), void *callback_data); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_callback_cli_cert(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_ssl_context_t *, const bctbx_list_t *), void *callback_data); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_ca_chain(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *ca_chain); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_own_cert(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_ciphersuites(bctbx_ssl_config_t *ssl_config,const int *ciphersuites); /***** DTLS-SRTP functions *****/ BCTBX_PUBLIC bctbx_dtls_srtp_profile_t bctbx_ssl_get_dtls_srtp_protection_profile(bctbx_ssl_context_t *ssl_ctx); BCTBX_PUBLIC int32_t bctbx_ssl_config_set_dtls_srtp_protection_profiles(bctbx_ssl_config_t *ssl_config, const bctbx_dtls_srtp_profile_t *profiles, size_t profiles_number); BCTBX_PUBLIC int32_t bctbx_ssl_get_dtls_srtp_key_material(bctbx_ssl_config_t *ssl_ctx, uint8_t *output, size_t *output_length); BCTBX_PUBLIC uint8_t bctbx_dtls_srtp_supported(void); BCTBX_PUBLIC void bctbx_ssl_set_mtu(bctbx_ssl_context_t *ssl_ctx, uint16_t mtu); /*****************************************************************************/ /***** Key exchanges defined algorithms *****/ /*****************************************************************************/ /** * @brief Return a 32 bits unsigned integer, each bit set to one matches an * available key agreement algorithm as defined in bctoolbox/include/crypto.h * * @return An unsigned integer of 32 flags matching key agreement algos */ BCTBX_PUBLIC uint32_t bctbx_key_agreement_algo_list(void); /*****************************************************************************/ /***** Diffie-Hellman-Merkle key exchange *****/ /*****************************************************************************/ /** * @brief Context for the Diffie-Hellman-Merkle key exchange * Use RFC3526 values for G and P */ typedef struct bctbx_DHMContext_struct { uint8_t algo; /**< Algorithm used for the key exchange mapped to an int: BCTBX_DHM_2048, BCTBX_DHM_3072 */ uint16_t primeLength; /**< Prime number length in bytes(256 or 384)*/ uint8_t *secret; /**< the random secret (X), this field may not be used if the crypto module implementation already store this value in his context */ uint8_t secretLength; /**< in bytes */ uint8_t *key; /**< the key exchanged (G^Y)^X mod P */ uint8_t *self; /**< this side of the public exchange G^X mod P */ uint8_t *peer; /**< the other side of the public exchange G^Y mod P */ void *cryptoModuleData; /**< a context needed by the crypto implementation */ }bctbx_DHMContext_t; /** * * @brief Create a context for the DHM key exchange * This function will also instantiate the context needed by the actual implementation of the crypto module * * @param[in] DHMAlgo The algorithm type(BCTBX_DHM_2048 or BCTBX_DHM_3072) * @param[in] secretLength The length in byte of the random secret(X). * * @return The initialised context for the DHM calculation(must then be freed calling the destroyDHMContext function), NULL on error * */ BCTBX_PUBLIC bctbx_DHMContext_t *bctbx_CreateDHMContext(uint8_t DHMAlgo, uint8_t secretLength); /** * * @brief Generate the private secret X and compute the public value G^X mod P * G, P and X length have been set by previous call to DHM_CreateDHMContext * * @param[in/out] context DHM context, will store the public value in ->self after this call * @param[in] rngFunction pointer to a random number generator used to create the secret X * @param[in] rngContext pointer to the rng context if neeeded * */ BCTBX_PUBLIC void bctbx_DHMCreatePublic(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext); /** * * @brief Compute the secret key G^X^Y mod p * G^X mod P has been computed in previous call to DHMCreatePublic * G^Y mod P must have been set in context->peer * * @param[in/out] context Read the public values from context, export the key to context->key * @param[in] rngFunction Pointer to a random number generation function, used for blinding countermeasure, may be NULL * @param[in] rngContext Pointer to the RNG function context * */ BCTBX_PUBLIC void bctbx_DHMComputeSecret(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext); /** * * @brief Clean DHM context. Secret and key, if present, are erased from memory(set to 0) * * @param context The context to deallocate * */ BCTBX_PUBLIC void bctbx_DestroyDHMContext(bctbx_DHMContext_t *context); /*****************************************************************************/ /***** Elliptic Curve Diffie-Hellman-Merkle key exchange *****/ /*****************************************************************************/ /** * * @brief return TRUE if the Elliptic Curve Cryptography is available */ BCTBX_PUBLIC int bctbx_crypto_have_ecc(void); /** * @brief Context for the EC Diffie-Hellman-Merkle key exchange on curve 25519 and 448 * Use RFC7748 for base points values */ typedef struct bctbx_ECDHContext_struct { uint8_t algo; /**< Algorithm used for the key exchange mapped to an int: BCTBX_ECDH_X25519, BCTBX_ECDH_X448 */ uint16_t pointCoordinateLength; /**< length in bytes of the point u-coordinate, can be 32 or 56 */ uint8_t *secret; /**< the random secret (scalar) used to compute public key and shared secret */ uint8_t secretLength; /**< in bytes, usually the same than pointCoordinateLength */ uint8_t *sharedSecret; /**< the key exchanged scalar multiplation of MULT(Self Secret, MULT(Peer Secret, BasePoint)), u-coordinate */ uint8_t *selfPublic; /**< this side of the public exchange: MULT(self secret, BasePoint), u-coordinate */ uint8_t *peerPublic; /**< the other side of the public exchange: MULT(peer secret, BasePoint), u-coordinate */ void *cryptoModuleData; /**< a context needed by the underlying crypto implementation - note if in use, most of the previous buffers could be store in it actually */ }bctbx_ECDHContext_t; /** * * @brief Create a context for the ECDH key exchange * * @param[in] ECDHAlgo The algorithm type(BCTBX_ECDH_X25519 or BCTBX_ECDH_X448) * * @return The initialised context for the ECDH calculation(must then be freed calling the destroyECDHContext function), NULL on error * */ BCTBX_PUBLIC bctbx_ECDHContext_t *bctbx_CreateECDHContext(const uint8_t ECDHAlgo); /** * * @brief Generate the private secret scalar and compute the public key MULT(scalar, BasePoint) * * @param[in/out] context ECDH context, will store the public value in ->selfPublic after this call, and secret in ->secret * @param[in] rngFunction pointer to a random number generator used to create the secret * @param[in] rngContext pointer to the rng context if neeeded * */ BCTBX_PUBLIC void bctbx_ECDHCreateKeyPair(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext); /** * * @brief Set the given secret key in the ECDH context * * @param[in/out] context ECDH context, will store the given secret key if length is matching the pre-setted algo for this context * @param[in] secret The buffer holding the secret, is duplicated in the ECDH context * @param[in] secretLength Length of previous buffer, must match the algo type setted at context creation */ BCTBX_PUBLIC void bctbx_ECDHSetSecretKey(bctbx_ECDHContext_t *context, const uint8_t *secret, const size_t secretLength); /** * * @brief Set the given self public key in the ECDH context * Warning: no check if it matches the private key value * * @param[in/out] context ECDH context, will store the given self public key if length is matching the pre-setted algo for this context * @param[in] selfPublic The buffer holding the self public key, is duplicated in the ECDH context * @param[in] selfPublicLength Length of previous buffer, must match the algo type setted at context creation */ BCTBX_PUBLIC void bctbx_ECDHSetSelfPublicKey(bctbx_ECDHContext_t *context, const uint8_t *selfPublic, const size_t selfPublicLength); /** * * @brief Set the given peer public key in the ECDH context * * @param[in/out] context ECDH context, will store the given peer public key if length is matching the pre-setted algo for this context * @param[in] peerPublic The buffer holding the peer public key, is duplicated in the ECDH context * @param[in] peerPublicLength Length of previous buffer, must match the algo type setted at context creation */ BCTBX_PUBLIC void bctbx_ECDHSetPeerPublicKey(bctbx_ECDHContext_t *context, const uint8_t *peerPublic, const size_t peerPublicLength); /** * * @brief Derive the public key from the secret setted in context and using preselected algo, following RFC7748 * * @param[in/out] context The context holding algo setting and secret, used to store public key */ BCTBX_PUBLIC void bctbx_ECDHDerivePublicKey(bctbx_ECDHContext_t *context); /** * * @brief Compute the shared secret MULT(secret, peer Public) * ->peerPublic, containing MULT(peerSecret, basePoint) must have been set before this call in context * * @param[in/out] context Read the public values from context, export the key to context->sharedSecret * @param[in] rngFunction Pointer to a random number generation function, used for blinding countermeasure, may be NULL * @param[in] rngContext Pointer to the RNG function context * */ BCTBX_PUBLIC void bctbx_ECDHComputeSecret(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext); /** * * @brief Clean ECDH context. Secret and key, if present, are erased from memory(set to 0) * * @param context The context to deallocate * */ BCTBX_PUBLIC void bctbx_DestroyECDHContext(bctbx_ECDHContext_t *context); /*****************************************************************************/ /***** EdDSA: signature and verify using Elliptic curves *****/ /*****************************************************************************/ /** * @brief Context for the EdDSA using curves 25519 and 448 */ typedef struct bctbx_EDDSAContext_struct { uint8_t algo; /**< Algorithm used for the key exchange mapped to an int: BCTBX_EDDSA_25519, BCTBX_EDDSA_448 */ uint16_t pointCoordinateLength; /**< length in bytes of a serialised point coordinate, can be 32 or 57 */ uint8_t *secretKey; /**< the random secret (scalar) used to compute public key and message signature, is the same length than a serialised point coordinate */ uint8_t secretLength; /**< in bytes, usually the same than pointCoordinateLength */ uint8_t *publicKey; /**< MULT(HASH(secretKey), BasePoint), serialised coordinate */ void *cryptoModuleData; /**< a context needed by the underlying crypto implementation - note if in use, most of the previous buffers could be store in it actually */ }bctbx_EDDSAContext_t; /** * * @brief Create a context for the EdDSA sign/verify * * @param[in] EDDSAAlgo The algorithm type(BCTBX_EDDSA_25519 or BCTBX_EDDSA_448) * * @return The initialised context for the EDDSA calculation(must then be freed calling the destroyEDDSAContext function), NULL on error * */ BCTBX_PUBLIC bctbx_EDDSAContext_t *bctbx_CreateEDDSAContext(uint8_t EDDSAAlgo); /** * * @brief Generate the private secret scalar and compute the public MULT(scalar, BasePoint) * * @param[in/out] context EDDSA context, will store the public value in ->publicKey after this call, and secret in ->secretKey * @param[in] rngFunction pointer to a random number generator used to create the secret * @param[in] rngContext pointer to the rng context if neeeded * */ BCTBX_PUBLIC void bctbx_EDDSACreateKeyPair(bctbx_EDDSAContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext); /** * * @brief Using the private secret scalar already set in context, compute the public MULT(scalar, BasePoint) * * @param[in/out] context EDDSA context, will store the public value in ->publicKey after this call, already have secret in ->secretKey * */ BCTBX_PUBLIC void bctbx_EDDSADerivePublicKey(bctbx_EDDSAContext_t *context); /** * * @brief Clean ECDH context. Secret and key, if present, are erased from memory(set to 0) * * @param context The context to deallocate * */ BCTBX_PUBLIC void bctbx_DestroyEDDSAContext(bctbx_EDDSAContext_t *context); /** * * @brief Sign the message given using private key and EdDSA algo set in context * * @param[in] context EDDSA context storing the algorithm to use(ed448 or ed25519) and the private key to use * @param[in] message The message to be signed * @param[in] messageLength Length of the message buffer * @param [in] associatedData A "context" for this signature of up to 255 bytes. * @param [in] associatedDataLength Length of the context. * @param[out] signature The signature * @param[in/out] signatureLength The size of the signature buffer as input, the size of the actual signature as output * */ BCTBX_PUBLIC void bctbx_EDDSA_sign(bctbx_EDDSAContext_t *context, const uint8_t *message, const size_t messageLength, const uint8_t *AssociatedData, const uint8_t associatedDataLength, uint8_t *signature, size_t *signatureLength); /** * * @brief Set a public key in a EDDSA context to be used to verify messages signature * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) * @param[in] publicKey The public to store in context * @param[in] publicKeyLength The length of previous buffer */ BCTBX_PUBLIC void bctbx_EDDSA_setPublicKey(bctbx_EDDSAContext_t *context, const uint8_t *publicKey, const size_t publicKeyLength); /** * * @brief Set a private key in a EDDSA context to be used to sign message * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) * @param[in] secretKey The secret to store in context * @param[in] secretKeyLength The length of previous buffer */ BCTBX_PUBLIC void bctbx_EDDSA_setSecretKey(bctbx_EDDSAContext_t *context, const uint8_t *secretKey, const size_t secretKeyLength); /** * * @brief Use the public key set in context to verify the given signature and message * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) and public key * @param[in] message Message to verify * @param[in] messageLength Length of the message buffer * @param [in] associatedData A "context" for this signature of up to 255 bytes. * @param [in] associatedDataLength Length of the context. * @param[in] signature The signature * @param[in] signatureLength The size of the signature buffer * * @return BCTBX_VERIFY_SUCCESS or BCTBX_VERIFY_FAILED */ BCTBX_PUBLIC int bctbx_EDDSA_verify(bctbx_EDDSAContext_t *context, const uint8_t *message, size_t messageLength, const uint8_t *associatedData, const uint8_t associatedDataLength, const uint8_t *signature, size_t signatureLength); /** * * @brief Convert a EDDSA private key to a ECDH private key * pass the EDDSA private key through the hash function used in EdDSA * * @param[in] ed Context holding the current private key to convert * @param[out] x Context to store the private key for x25519 key exchange */ BCTBX_PUBLIC void bctbx_EDDSA_ECDH_privateKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x); #define BCTBX_ECDH_ISPEER 0 #define BCTBX_ECDH_ISSELF 1 /** * * @brief Convert a EDDSA public key to a ECDH public key * point conversion : montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p * * @param[in] ed Context holding the current public key to convert * @param[out] x Context to store the public key for x25519 key exchange * @param[in] isSelf Flag to decide where to store the public key in context: BCTBX_ECDH_ISPEER or BCTBX_ECDH_ISPEER */ BCTBX_PUBLIC void bctbx_EDDSA_ECDH_publicKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x, uint8_t isSelf); /*****************************************************************************/ /***** Hashing *****/ /*****************************************************************************/ /* * @brief SHA512 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 64 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_sha512(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output); /* * @brief SHA384 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_sha384(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output); /** * @brief SHA256 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, SHA256 output is truncated to the hashLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_sha256(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output); /** * @brief HMAC-SHA512 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 64 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_hmacSha512(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output); /** * @brief HMAC-SHA384 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length in bytes * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_hmacSha384(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output); /** * @brief HMAC-SHA256 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length in bytes * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_hmacSha256(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output); /** * @brief HMAC-SHA1 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 20 bytes maximum * @param[out] output Output data buffer * */ BCTBX_PUBLIC void bctbx_hmacSha1(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output); /** * @brief MD5 wrapper * output = md5(input) * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[out] output Output data buffer. * */ BCTBX_PUBLIC void bctbx_md5(const uint8_t *input, size_t inputLength, uint8_t output[16]); /*****************************************************************************/ /***** Encryption/Decryption *****/ /*****************************************************************************/ typedef struct bctbx_aes_gcm_context_struct bctbx_aes_gcm_context_t; /** * @Brief AES-GCM encrypt and tag buffer * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] plainText buffer to be encrypted * @param[in] plainTextLength Length in bytes of buffer to be encrypted * @param[in] authenticatedData Buffer holding additional data to be used in tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[out] tag Buffer holding the generated tag * @param[in] tagLength Requested length for the generated tag * @param[out] output Buffer holding the output, shall be at least the length of plainText buffer * * @return 0 on success, crypto library error code otherwise */ BCTBX_PUBLIC int32_t bctbx_aes_gcm_encrypt_and_tag(const uint8_t *key, size_t keyLength, const uint8_t *plainText, size_t plainTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t *tag, size_t tagLength, uint8_t *output); /** * @Brief AES-GCM decrypt, compute authentication tag and compare it to the one provided * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] cipherText Buffer to be decrypted * @param[in] cipherTextLength Length in bytes of buffer to be decrypted * @param[in] authenticatedData Buffer holding additional data to be used in auth tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] tag Buffer holding the authentication tag * @param[in] tagLength Length in bytes for the authentication tag * @param[out] output Buffer holding the output, shall be at least the length of cipherText buffer * * @return 0 on succes, BCTBX_ERROR_AUTHENTICATION_FAILED if tag doesn't match or crypto library error code */ BCTBX_PUBLIC int32_t bctbx_aes_gcm_decrypt_and_auth(const uint8_t *key, size_t keyLength, const uint8_t *cipherText, size_t cipherTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, const uint8_t *tag, size_t tagLength, uint8_t *output); /** * @Brief create and initialise an AES-GCM encryption context * * @param[in] key encryption key * @param[in] keyLength key buffer length, in bytes, must be 16,24 or 32 * @param[in] authenticatedData Buffer holding additional data to be used in tag computation * @param[in] authenticatedDataLength additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] mode Operation mode : BCTBX_GCM_ENCRYPT or BCTBX_GCM_DECRYPT * * @return a pointer to the created context, to be freed using bctbx_aes_gcm_finish() */ BCTBX_PUBLIC bctbx_aes_gcm_context_t *bctbx_aes_gcm_context_new(const uint8_t *key, size_t keyLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t mode); /** * @Brief AES-GCM encrypt or decrypt a chunk of data * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[in] input buffer holding the input data * @param[in] inputLength length of the input data * @param[out] output buffer to store the output data (same length as input one) * * @return 0 on success, crypto library error code otherwise */ BCTBX_PUBLIC int32_t bctbx_aes_gcm_process_chunk(bctbx_aes_gcm_context_t *context, const uint8_t *input, size_t inputLength, uint8_t *output); /** * @Brief Conclude a AES-GCM encryption stream, generate tag if requested, free resources * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[out] tag a buffer to hold the authentication tag. Can be NULL if tagLength is 0 * @param[in] tagLength length of requested authentication tag, max 16 * * @return 0 on success, crypto library error code otherwise */ BCTBX_PUBLIC int32_t bctbx_aes_gcm_finish(bctbx_aes_gcm_context_t *context, uint8_t *tag, size_t tagLength); /** * @brief Wrapper for AES-128 in CFB128 mode encryption * Both key and IV must be 16 bytes long * * @param[in] key encryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ BCTBX_PUBLIC void bctbx_aes128CfbEncrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /** * @brief Wrapper for AES-128 in CFB128 mode decryption * Both key and IV must be 16 bytes long * * @param[in] key decryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ BCTBX_PUBLIC void bctbx_aes128CfbDecrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /** * @brief Wrapper for AES-256 in CFB128 mode encryption * The key must be 32 bytes long and the IV must be 16 bytes long * * @param[in] key encryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ BCTBX_PUBLIC void bctbx_aes256CfbEncrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /** * @brief Wrapper for AES-256 in CFB128 mode decryption * The key must be 32 bytes long and the IV must be 16 bytes long * * @param[in] key decryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ BCTBX_PUBLIC void bctbx_aes256CfbDecrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output); /** * @brief encrypt the file in input buffer for linphone encrypted file transfer * * This function must be called with NULL in the plain text to conclude the encryption. * At this last call, if a cipher buffer is provided with non 0 length, it will get an authentication tag of the requested size (max 16) * * @param[in/out] cryptoContext a context already initialized using bctbx_aes_gcm_context_new (created if NULL) * @param[in] key encryption key * @param[in] length buffer size * @param[in] plain buffer holding the input data * @param[out] cipher buffer to store the output data (cipher or authentication tag) */ BCTBX_PUBLIC int bctbx_aes_gcm_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher); /** * @brief decrypt the file in input buffer for linphone encrypted file transfer * * This function must be called with NULL in the cipher text to conclude the encryption. * At this last call, if a plain buffer is provided with non 0 length, it will get the authentication tag of length bytes (max 16) * * @param[in/out] cryptoContext a context already initialized using bctbx_aes_gcm_context_new * @param[in] key encryption key * @param[in] length input buffer size * @param[out] plain buffer holding the output data (plain text or tag) * @param[in] cipher buffer to store the input data. WARNING: size must be a multiple of 16 bytes */ BCTBX_PUBLIC int bctbx_aes_gcm_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher); /*****************************************************************************/ /***** Cleaning *****/ /*****************************************************************************/ /** * @brief force a buffer values to zero in a way that shall prevent the compiler from optimizing it out * * @param[in/out] buffer the buffer to be cleared * @param[in] size buffer size */ BCTBX_PUBLIC void bctbx_clean(void *buffer, size_t size); #ifdef __cplusplus } #endif #endif /* BCTBX_CRYPTO_H */ bctoolbox-5.2.0/include/bctoolbox/crypto.hh000066400000000000000000000242701434566643100210110ustar00rootroot00000000000000/* * Copyright (c) 2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_CRYPTO_HH #define BCTBX_CRYPTO_HH #include #include #include #include #include "crypto.h" namespace bctoolbox { /** * @brief Random number generator interface * * This wrapper provides an interface to a RNG. * Two ways to get some random numbers: * - calling the static class functions(cRandomize) : do not use this to feed cryptographic functions * - instanciate a RNG object and call the randomize method : use this one for cryptographic quality random * * Any call (including creation), may throw an exception if some error are detected on the random source */ class RNG { public: /** * fill a buffer with random numbers * @param[in,out] buffer The buffer to be filled with random (callers responsability to allocate memory) * @param[in] size size in bytes of the random generated, buffer must be at least of this size **/ void randomize(uint8_t *buffer, const size_t size); /** * return a random vector of given size * @param[in] size size in bytes of the random generated **/ std::vector randomize(const size_t size); /** * generates a 32 bits random unsigned number **/ uint32_t randomize(); /** * fill a buffer with random numbers * @param[in,out] buffer The buffer to be filled with random (callers responsability to allocate memory) * @param[in] size size in bytes of the random generated, buffer must be at least of this size * * @note This function uses a shared RNG context, do not use it to generate sensitive material **/ static void cRandomize(uint8_t *buffer, size_t size); /** * generates a 32 bits random unsigned number * * @note This function uses a shared RNG context, do not use it to generate sensitive material **/ static uint32_t cRandomize(); RNG(); ~RNG(); private: struct Impl; std::unique_ptr pImpl; static std::unique_ptr pImplClass; }; //class RNG /*****************************************************************************/ /*** Hash related function ***/ /*****************************************************************************/ /** * @brief SHA1 buffer size definition */ struct SHA1 { /// maximum output size for SHA1 is 20 bytes static constexpr size_t ssize() {return 20;} }; /** * @brief SHA256 buffer size definition */ struct SHA256 { /// maximum output size for SHA256 is 32 bytes static constexpr size_t ssize() {return 32;} }; /** * @brief SHA384 buffer size definition */ struct SHA384 { /// maximum output size for SHA384 is 48 bytes static constexpr size_t ssize() {return 48;} }; /** * @brief SHA512 buffer size definition */ struct SHA512 { /// maximum output size for SHA512 is 64 bytes static constexpr size_t ssize() {return 64;} }; /** * @brief templated HMAC * * @tparam hashAlgo the hash algorithm used: SHA256, SHA384, SHA512 * * @param[in] key HMAC key * @param[in] input HMAC input * * @return an array of size matching the selected hash algorithm output size * */ template std::vector HMAC(const std::vector &key, const std::vector &input); /* declare template specialisations */ template <> std::vector HMAC(const std::vector &key, const std::vector &input); template <> std::vector HMAC(const std::vector &key, const std::vector &input); template <> std::vector HMAC(const std::vector &key, const std::vector &input); template <> std::vector HMAC(const std::vector &key, const std::vector &input); /** * @brief HKDF as described in RFC5869 * @par Compute: * @code{.unparsed} * PRK = HMAC-Hash(salt, IKM) * * N = ceil(L/HashLen) * T = T(1) | T(2) | T(3) | ... | T(N) * OKM = first L octets of T * * where: * T(0) = empty string (zero length) * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) * ... * @endcode * * @tparam hashAlgo the hash algorithm to use * @tparam infoType the info parameter type : can be passed as a string or a std::vector * * @param[in] salt salt * @param[in] ikm input key material * @param[in] info a info string or buffer * @param[in] okmSize requested amount of data. (L in the RFC doc) * * @return the output key material * */ template std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t okmSize); template std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t okmSize); /* declare template specialisations */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize); template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize); template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize); template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize); template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize); template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize); /************************ AEAD interface *************************************/ // AEAD function defines /** * @brief AES256GCM buffers size definition */ struct AES256GCM128 { /// key size is 32 bytes static constexpr size_t keySize(void) {return 32;}; /// tag size is 16 bytes static constexpr size_t tagSize(void) {return 16;}; }; /** * @brief Encrypt and tag using scheme given as template parameter * * @param[in] key Encryption key * @param[in] IV Initialisation vector * @param[in] plain Plain text * @param[in] AD Additional data used in tag computation * @param[out] tag Generated authentication tag * @return the cipher text */ template std::vector AEADEncrypt(const std::vector &key, const std::vector IV, const std::vector &plain, const std::vector &AD, std::vector &tag); /** * @brief Authenticate and Decrypt using scheme given as template parameter * * @param[in] key Encryption key * @param[in] IV Initialisation vector * @param[in] cipher Cipher text * @param[in] AD Additional data used in tag computation * @param[in] tag Authentication tag * @param[out] plain A vector to store the plain text * * @return true if authentication tag match and decryption was successful */ template bool AEADDecrypt(const std::vector &key, const std::vector &IV, const std::vector &cipher, const std::vector &AD, const std::vector &tag, std::vector &plain); /* declare AEAD template specialisations : AES256-GCM with 128 bits auth tag*/ template <> std::vector AEADEncrypt(const std::vector &key, const std::vector IV, const std::vector &plain, const std::vector &AD, std::vector &tag); template <> bool AEADDecrypt(const std::vector &key, const std::vector &IV, const std::vector &cipher, const std::vector &AD, const std::vector &tag, std::vector &plain); /************************** AES Key Wrap Algorithm ***************************/ enum class AesId {AES128, AES192, AES256}; /** * @brief AES Key Wrap with Padding Algorithm described by the RFC 5649 Section 4.1 : https://datatracker.ietf.org/doc/html/rfc5649#section-4.1 * Implementation of the alternative description of the algorithm * Used to encrypt the AES keys * Works with AES-128, AES-192 and AES-256 * * @param[in] plaintext AES key that we want to encrypt OR a plaintext * @param[in] key Key-encryption key (KEK) * @param[out] ciphertext The AES key encrypted OR the ciphertext * @param[in] aes_id Id of the AES version used here * * @return 0 if no problem, BCTBX_ERROR_INVALID_INPUT_DATA otherwise */ int AES_key_wrap(const std::vector &plaintext, const std::vector &key, std::vector &ciphertext, AesId id); /** * @brief AES Key Unwrap with Padding Algorithm described by the RFC 5649 Section 4.2 : https://datatracker.ietf.org/doc/html/rfc5649#section-4.2 * Implementation of the alternative description of the algorithm * Used to decrypt the AES keys * Works with AES-128, AES-192 and AES-256 * * @param[in] ciphertext AES key that we want to decrypt OR a ciphertext * @param[in] key Key-encryption key (KEK) * @param[out] plaintext The AES key decrypted OR the plaintext * @param[in] aes_id Id of the AES version used here * * @return 0 if no problem, {BCTBX_ERROR_INVALID_INPUT_DATA, BCTBX_ERROR_UNSPECIFIED_ERROR} otherwise */ int AES_key_unwrap(const std::vector &ciphertext, const std::vector &key, std::vector &plaintext, AesId id); } // namespace bctoolbox #endif // BCTBX_CRYPTO_HH bctoolbox-5.2.0/include/bctoolbox/defs.h000066400000000000000000000021301434566643100202310ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_DEFS_H_ #define BCTBX_DEFS_H_ /* Macro telling GCC that a 'break' statement has been deliberately omitted * in switch block */ #ifndef BCTBX_NO_BREAK #if defined(__GNUC__) && __GNUC__ >= 7 #define BCTBX_NO_BREAK __attribute__((fallthrough)) #else #define BCTBX_NO_BREAK #endif // __GNUC__ #endif // BCTBX_NO_BREAK #endif /* BCTBX_DEFS_H_ */ bctoolbox-5.2.0/include/bctoolbox/exception.hh000066400000000000000000000041251434566643100214640ustar00rootroot00000000000000/* bctoolbox Copyright (C) 2016 Belledonne Communications SARL. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #ifndef exception_h #define exception_h #include #include #include "bctoolbox/port.h" #ifdef _WIN32 #pragma warning (push) #pragma warning (disable : 4275 4251) #endif /** * @brief General pupose exception saving backtrace. * * sample of use: * try { * throw BCTBX_EXCEPTION << "Hello, this is my exception"; * } catch (BctbxException e&) { * BCTOOLBOX_SLOGD("mylogdomain") << "Exception cauth"<< e; * } * * */ class BCTBX_PUBLIC BctbxException : public std::exception { public: BctbxException(const std::string &message = ""); BctbxException(const BctbxException &other); virtual ~BctbxException() = default; /** * print stack strace to stderr * */ void printStackTrace() const; void printStackTrace(std::ostream &os) const; const char *what() const noexcept override; const std::string& str() const; /* same as osstringstream, but as osstream does not have cp contructor, BctbxException can't inherit from * osstream*/ template BctbxException &operator<<(const T2 &val) { mOs << val; return *this; } private: void *mArray[20]; size_t mSize; std::ostringstream mOs; mutable std::string mMessage; }; BCTBX_PUBLIC std::ostream &operator<<(std::ostream &__os, const BctbxException &e); #define BCTBX_EXCEPTION BctbxException() << " " << __FILE__ << ":" << __LINE__ << " " #ifdef _WIN32 #pragma warning (pop) #endif #endif /* exception_h */ bctoolbox-5.2.0/include/bctoolbox/ios_utils.hh000066400000000000000000000033161434566643100215010ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 . */ #pragma once #include namespace bctoolbox { class IOSUtilsInterface { public: virtual unsigned long beginBackgroundTask(const char *name, std::function cb) = 0; virtual void endBackgroundTask(unsigned long id) = 0; virtual bool isApplicationStateActive() = 0; virtual ~IOSUtilsInterface() = default; }; class IOSUtils { public: unsigned long beginBackgroundTask(const char *name, std::function cb); void endBackgroundTask(unsigned long id); bool isApplicationStateActive(); bool isApp(); int getOSMajorVersion() const; static IOSUtils& getUtils(); IOSUtils(const IOSUtils&) = delete; IOSUtils& operator=(const IOSUtils&) = delete; ~IOSUtils(); private: void *mHandle; IOSUtilsInterface *mUtils; bool mIsApp; static std::unique_ptr sInstance; IOSUtils(); void openDynamicLib(); void *loadSymbol(const char *symbol); }; } //namespace bctoolbox bctoolbox-5.2.0/include/bctoolbox/list.h000066400000000000000000000107271434566643100202760ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_LIST_H_ #define BCTBX_LIST_H_ #include "bctoolbox/port.h" #ifdef __cplusplus extern "C"{ #endif typedef struct _bctbx_list { struct _bctbx_list *next; struct _bctbx_list *prev; void *data; } bctbx_list_t; typedef int (*bctbx_compare_func)(const void *, const void*); typedef void (*bctbx_list_iterate_func)(void *); typedef void (*bctbx_list_iterate2_func)(void *, void *); typedef void (*bctbx_list_free_func)(void *); typedef void* (*bctbx_list_copy_func)(void *); BCTBX_PUBLIC bctbx_list_t * bctbx_list_new(void *data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_append(bctbx_list_t * elem, void * data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_append_link(bctbx_list_t * elem, bctbx_list_t *new_elem); BCTBX_PUBLIC bctbx_list_t * bctbx_list_prepend(bctbx_list_t * elem, void * data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_prepend_link(bctbx_list_t* elem, bctbx_list_t *new_elem); BCTBX_PUBLIC bctbx_list_t * bctbx_list_last_elem(const bctbx_list_t *l); BCTBX_PUBLIC bctbx_list_t * bctbx_list_first_elem(const bctbx_list_t *l); BCTBX_PUBLIC bctbx_list_t * bctbx_list_free(bctbx_list_t * elem); BCTBX_PUBLIC bctbx_list_t * bctbx_list_concat(bctbx_list_t * first, bctbx_list_t * second); BCTBX_PUBLIC bctbx_list_t * bctbx_list_remove(bctbx_list_t * first, void *data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_remove_custom(bctbx_list_t *first, bctbx_compare_func compare_func, const void *user_data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_pop_front(bctbx_list_t *list, void **front_data); BCTBX_PUBLIC size_t bctbx_list_size(const bctbx_list_t * first); BCTBX_PUBLIC void bctbx_list_for_each(const bctbx_list_t * list, bctbx_list_iterate_func func); BCTBX_PUBLIC void bctbx_list_for_each2(const bctbx_list_t * list, bctbx_list_iterate2_func func, void *user_data); /** * Removes the element pointed by elem from the list. The element itself is not freed, allowing * to be chained in another list for example. * Use bctbx_list_erase_link() if you simply want to delete an element of a list. **/ BCTBX_PUBLIC bctbx_list_t * bctbx_list_unlink(bctbx_list_t * list, bctbx_list_t * elem); /** * Delete the element pointed by 'elem' from the list. **/ BCTBX_PUBLIC bctbx_list_t * bctbx_list_erase_link(bctbx_list_t * list, bctbx_list_t * elem); BCTBX_PUBLIC bctbx_list_t * bctbx_list_find(bctbx_list_t *list, const void *data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_free(bctbx_list_t *list); /*frees list elements and associated data, using the supplied function pointer*/ BCTBX_PUBLIC bctbx_list_t * bctbx_list_free_with_data(bctbx_list_t *list, bctbx_list_free_func freefunc); BCTBX_PUBLIC bctbx_list_t * bctbx_list_find_custom(const bctbx_list_t * list, bctbx_compare_func cmp, const void *user_data); BCTBX_PUBLIC void * bctbx_list_nth_data(const bctbx_list_t * list, int index); BCTBX_PUBLIC int bctbx_list_position(const bctbx_list_t * list, bctbx_list_t * elem); BCTBX_PUBLIC int bctbx_list_index(const bctbx_list_t * list, void *data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_insert_sorted(bctbx_list_t * list, void *data, bctbx_compare_func cmp); BCTBX_PUBLIC bctbx_list_t * bctbx_list_insert(bctbx_list_t * list, bctbx_list_t * before, void *data); BCTBX_PUBLIC bctbx_list_t * bctbx_list_copy(const bctbx_list_t * list); /*copy list elements and associated data, using the supplied function pointer*/ BCTBX_PUBLIC bctbx_list_t* bctbx_list_copy_with_data(const bctbx_list_t* list, bctbx_list_copy_func copyfunc); /*Same as bctbx_list_copy_with_data but in reverse order*/ BCTBX_PUBLIC bctbx_list_t* bctbx_list_copy_reverse_with_data(const bctbx_list_t* list, bctbx_list_copy_func copyfunc); BCTBX_PUBLIC bctbx_list_t* bctbx_list_next(const bctbx_list_t *elem); BCTBX_PUBLIC void* bctbx_list_get_data(const bctbx_list_t *elem); #ifdef __cplusplus } #endif #endif /* BCTLBX_LIST_H_ */ bctoolbox-5.2.0/include/bctoolbox/logging.h000066400000000000000000000305761434566643100207550ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ /** * \file logging.h * \brief Logging API. * **/ #ifndef BCTBX_LOGGING_H #define BCTBX_LOGGING_H #include #include "bctoolbox/list.h" /** * The BCTBX_LOG_DOMAIN macro should be defined with a preprocessor directive (ex: -DBCTBX_LOG_DOMAIN="my-component") in every * software entity (application, library, sub-parts of software etc) using the bctoolbox log facility, so that all invocations of the * bctbx_message(), bctbx_warning(), bctbx_error(), bctbx_fatal() outputs their log within the domain title of the software part there are compiled in. * It SHALL not be defined in any public header, otherwise it will conflict with upper layer using the logging facility for their own domain. * As a special exception, bctoolbox defines the log domain to be "bctbx" if no preprocessor directive defines it. * As a result, bctboolbox owns logs will be output into the "bctbx" domain. **/ #ifndef BCTBX_LOG_DOMAIN #define BCTBX_LOG_DOMAIN "bctbx" #endif #ifdef __cplusplus extern "C" { #endif typedef enum { BCTBX_LOG_DEBUG=1, BCTBX_LOG_TRACE=1<<1, BCTBX_LOG_MESSAGE=1<<2, BCTBX_LOG_WARNING=1<<3, BCTBX_LOG_ERROR=1<<4, BCTBX_LOG_FATAL=1<<5, BCTBX_LOG_LOGLEV_END=1<<6 } BctbxLogLevel; typedef struct _bctbx_log_handler_t bctbx_log_handler_t; typedef void (*BctbxLogFunc)(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args); typedef void (*BctbxLogHandlerFunc)(void *info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args); typedef void (*BctbxLogHandlerDestroyFunc)(bctbx_log_handler_t *handler); /* initialise logging functions, add default log handler for stdout output. @param[in] bool_t create : No longer used, always created with a default logger to stdout. */ BCTBX_PUBLIC void bctbx_init_logger(bool_t create); /* free logging memory */ BCTBX_PUBLIC void bctbx_uninit_logger(void); /* Default functions to free log handlers @param[in] bctbx_log_handler_t* handler : the handler to free */ BCTBX_PUBLIC void bctbx_logv_out_destroy(bctbx_log_handler_t *handler); BCTBX_PUBLIC void bctbx_logv_file_destroy(bctbx_log_handler_t *handler); /* Function to create a log handler @param[in] BctbxLogHandlerFunc func : the function to call to handle a new line of log @param[in] BctbxLogHandlerDestroyFunc destroy : the function to call to free this handler particuler its user_info field @param[in] void* user_info : complementary information to handle the logs if needed @return a new bctbx_log_handler_t */ BCTBX_PUBLIC bctbx_log_handler_t* bctbx_create_log_handler(BctbxLogHandlerFunc func, BctbxLogHandlerDestroyFunc destroy, void* user_data); /* Function to create a file log handler @param[in] uint64_t max_size : the maximum size of the log file before rotating to a new one (if 0 then no rotation) @param[in] const char* path : the path where to put the log files @param[in] const char* name : the name of the log files @param[in] FILE* f : the file where to write the logs @return a new bctbx_log_handler_t */ BCTBX_PUBLIC bctbx_log_handler_t* bctbx_create_file_log_handler(uint64_t max_size, const char* path, const char* name); /** * @brief Request reopening of the log file. * @param[in] file_log_handler The log handler whose file will be reopened. * @note This function is thread-safe and reopening is done asynchronously. */ BCTBX_PUBLIC void bctbx_file_log_handler_reopen(bctbx_log_handler_t *file_log_handler); /* set domain the handler is limited to. NULL for ALL*/ BCTBX_PUBLIC void bctbx_log_handler_set_domain(bctbx_log_handler_t * log_handler,const char *domain); BCTBX_PUBLIC void bctbx_log_handler_set_user_data(bctbx_log_handler_t*, void* user_data); BCTBX_PUBLIC void *bctbx_log_handler_get_user_data(const bctbx_log_handler_t* log_handler); BCTBX_PUBLIC void bctbx_add_log_handler(bctbx_log_handler_t* handler); BCTBX_PUBLIC void bctbx_remove_log_handler(bctbx_log_handler_t* handler); /* * Set a callback function to render logs for the default handler. */ BCTBX_PUBLIC void bctbx_set_log_handler(BctbxLogFunc func); /* * Same as bctbx_set_log_handler but only for a domain. NULL for all. * Be careful that if domain is specified, the default log handler will no longer output logs for other domains. */ BCTBX_PUBLIC void bctbx_set_log_handler_for_domain(BctbxLogFunc func, const char* domain); /*Convenient function that creates a static log handler logging into supplied FILE argument. Despite it is not recommended to use it in libraries, it can be useful for simple test programs.*/ BCTBX_PUBLIC void bctbx_set_log_file(FILE* f); BCTBX_PUBLIC bctbx_list_t* bctbx_get_log_handlers(void); BCTBX_PUBLIC void bctbx_logv_out(const char *domain, BctbxLogLevel level, const char *fmt, va_list args); BCTBX_PUBLIC void bctbx_logv_file(void* user_info, const char *domain, BctbxLogLevel level, const char *fmt, va_list args); /* * Returns 1 if the log level 'level' is enabled for the calling thread, otherwise 0. * This gives the condition to decide to output a log. */ BCTBX_PUBLIC int bctbx_log_level_enabled(const char *domain, BctbxLogLevel level); BCTBX_PUBLIC void bctbx_logv(const char *domain, BctbxLogLevel level, const char *fmt, va_list args); /** * Flushes the log output queue. * WARNING: Must be called from the thread that has been defined with bctbx_set_log_thread_id(). */ BCTBX_PUBLIC void bctbx_logv_flush(void); /** * Activate all log level greater or equal than specified level argument. **/ BCTBX_PUBLIC void bctbx_set_log_level(const char *domain, BctbxLogLevel level); BCTBX_PUBLIC void bctbx_set_log_level_mask(const char *domain, int levelmask); BCTBX_PUBLIC unsigned int bctbx_get_log_level_mask(const char *domain); /** * Set a specific log level for the calling thread for domain. * When domain is NULL, the log level applies to all domains. */ BCTBX_PUBLIC void bctbx_set_thread_log_level(const char *domain, BctbxLogLevel level); /** * Clears the specific log level set for the calling thread by bctbx_set_thread_log_level(). * After calling this function, the global (not per thread) log level will apply to * logs printed by this thread. */ BCTBX_PUBLIC void bctbx_clear_thread_log_level(const char *domain); /** * Tell oRTP the id of the thread used to output the logs. * This is meant to output all the logs from the same thread to prevent deadlock problems at the application level. * @param[in] thread_id The id of the thread that will output the logs (can be obtained using bctbx_thread_self()). */ BCTBX_PUBLIC void bctbx_set_log_thread_id(unsigned long thread_id); #ifdef __GNUC__ #define CHECK_FORMAT_ARGS(m,n) __attribute__((format(printf,m,n))) #else #define CHECK_FORMAT_ARGS(m,n) #endif #ifdef __clang__ /*in case of compile with -g static inline can produce this type of warning*/ #pragma GCC diagnostic ignored "-Wunused-function" #endif #ifdef BCTBX_DEBUG_MODE static BCTBX_INLINE void CHECK_FORMAT_ARGS(1,2) bctbx_debug(const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG, fmt, args); va_end (args); } #else #define bctbx_debug(...) #endif #ifdef BCTBX_NOMESSAGE_MODE #define bctbx_log(...) #define bctbx_message(...) #define bctbx_warning(...) #else static BCTBX_INLINE void bctbx_log(const char* domain, BctbxLogLevel lev, const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(domain, lev, fmt, args); va_end (args); } static BCTBX_INLINE void CHECK_FORMAT_ARGS(1,2) bctbx_message(const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(BCTBX_LOG_DOMAIN, BCTBX_LOG_MESSAGE, fmt, args); va_end (args); } static BCTBX_INLINE void CHECK_FORMAT_ARGS(1,2) bctbx_warning(const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(BCTBX_LOG_DOMAIN, BCTBX_LOG_WARNING, fmt, args); va_end (args); } #endif static BCTBX_INLINE void CHECK_FORMAT_ARGS(1,2) bctbx_error(const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(BCTBX_LOG_DOMAIN, BCTBX_LOG_ERROR, fmt, args); va_end (args); } static BCTBX_INLINE void CHECK_FORMAT_ARGS(1,2) bctbx_fatal(const char *fmt,...) { va_list args; va_start (args, fmt); bctbx_logv(BCTBX_LOG_DOMAIN, BCTBX_LOG_FATAL, fmt, args); va_end (args); } #ifdef __QNX__ void bctbx_qnx_log_handler(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args); #endif #ifdef __cplusplus } #include #include #include #include #if !defined(_WIN32) && !defined(__QNX__) #include #endif namespace bctoolbox { namespace log { // Here we define our application severity levels. enum level { normal, trace, debug, info, warning, error, fatal }; // The formatting logic for the severity level template inline std::basic_ostream &operator<<(std::basic_ostream &strm, const bctoolbox::log::level &lvl) { static const char *const str[] = {"normal", "trace", "debug", "info", "warning", "error", "fatal"}; if (static_cast(lvl) < (sizeof(str) / sizeof(*str))) strm << str[lvl]; else strm << static_cast(lvl); return strm; } template inline std::basic_istream &operator>>(std::basic_istream &strm, bctoolbox::log::level &lvl) { static const char *const str[] = {"normal", "trace", "debug", "info", "warning", "error", "fatal"}; std::string s; strm >> s; for (unsigned int n = 0; n < (sizeof(str) / sizeof(*str)); ++n) { if (s == str[n]) { lvl = static_cast(n); return strm; } } // Parse error strm.setstate(std::ios_base::failbit); return strm; } } } #include class pumpstream { public: /* This constructor assumes that "domain" remains valid, which is a reasonable assumption because the pumpstream * is used through macros (below) where the usage is within single line of code. */ pumpstream(const char *domain, BctbxLogLevel level) : mDomain(domain), mLevel(level){ #ifndef BCTBX_DEBUG_MODE /* If debug mode is not enabled, the pumpstream shall do nothing if level requested is BCTBX_LOG_DEBUG. * bctbx_log_level_enabled() does not even need to be called. */ if (level == BCTBX_LOG_DEBUG) { mIslogLevelEnabled = false; return; } #endif mIslogLevelEnabled = bctbx_log_level_enabled(domain, mLevel); } ~pumpstream() { if (mIslogLevelEnabled) bctbx_log(mDomain, mLevel, "%s", mOstringstream.str().c_str()); } template friend pumpstream& operator<<(pumpstream& __os, _Tp&& __x); template friend pumpstream& operator<<(pumpstream&& __os, _Tp&& __x); friend pumpstream& operator<<(pumpstream& __os, std::ostream& (*pf)(std::ostream&)); private: std::ostringstream mOstringstream{}; bool mIslogLevelEnabled = false; const char* mDomain; const BctbxLogLevel mLevel; }; inline pumpstream& operator<<(pumpstream& pumpStream, std::ostream& (*pf)(std::ostream&)) { if(pumpStream.mIslogLevelEnabled) { pumpStream.mOstringstream << pf; } return pumpStream; } template inline pumpstream& operator<<(pumpstream& pumpStream, T&& x) { if(pumpStream.mIslogLevelEnabled) { pumpStream.mOstringstream << std::forward(x); } return pumpStream; } template inline pumpstream& operator<<(pumpstream&& pumpStream, T&& x) { if(pumpStream.mIslogLevelEnabled) { pumpStream.mOstringstream << std::forward(x); } return pumpStream; } #define BCTBX_SLOG(domain, thelevel) pumpstream(domain, thelevel) #define BCTBX_SLOGD BCTBX_SLOG(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG) #define BCTBX_SLOGI BCTBX_SLOG(BCTBX_LOG_DOMAIN, BCTBX_LOG_MESSAGE) #define BCTBX_SLOGW BCTBX_SLOG(BCTBX_LOG_DOMAIN, BCTBX_LOG_WARNING) #define BCTBX_SLOGE BCTBX_SLOG(BCTBX_LOG_DOMAIN, BCTBX_LOG_ERROR) #endif #endif bctoolbox-5.2.0/include/bctoolbox/map.h000066400000000000000000000140361434566643100200750ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_MMAP_H_ #define BCTBX_MMAP_H_ #include "bctoolbox/list.h" #include "bctoolbox/port.h" #ifdef __cplusplus extern "C"{ #endif typedef struct _bctbx_map_t bctbx_map_t; typedef struct _bctbx_pair_t bctbx_pair_t; typedef struct _bctbx_iterator_t bctbx_iterator_t; typedef struct _bctbx_mmap_ullong_t bctbx_mmap_ullong_t; typedef struct _bctbx_mmap_cchar_t bctbx_mmap_cchar_t; typedef void (*bctbx_map_free_func)(void *); /*map*/ BCTBX_PUBLIC bctbx_map_t *bctbx_mmap_ullong_new(void); BCTBX_PUBLIC bctbx_map_t *bctbx_mmap_cchar_new(void); BCTBX_PUBLIC void bctbx_mmap_ullong_delete(bctbx_map_t *mmap); BCTBX_PUBLIC void bctbx_mmap_cchar_delete(bctbx_map_t *mmap); BCTBX_PUBLIC void bctbx_mmap_ullong_delete_with_data(bctbx_map_t *mmap, bctbx_map_free_func freefunc); BCTBX_PUBLIC void bctbx_mmap_cchar_delete_with_data(bctbx_map_t *mmap, bctbx_map_free_func freefunc); #define bctbx_map_insert bctbx_map_ullong_insert BCTBX_PUBLIC void bctbx_map_ullong_insert(bctbx_map_t *map,const bctbx_pair_t *pair); BCTBX_PUBLIC void bctbx_map_cchar_insert(bctbx_map_t *map,const bctbx_pair_t *pair); /*same as insert, but also deleting pair*/ #define bctbx_map_insert_and_delete bctbx_map_ullong_insert_and_delete BCTBX_PUBLIC void bctbx_map_ullong_insert_and_delete(bctbx_map_t *map,bctbx_pair_t *pair); BCTBX_PUBLIC void bctbx_map_cchar_insert_and_delete(bctbx_map_t *map,bctbx_pair_t *pair); /*same as insert and deleting pair with a newly allocated it returned */ #define bctbx_map_insert_and_delete_with_returned_it bctbx_map_ullong_insert_and_delete_with_returned_it BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_ullong_insert_and_delete_with_returned_it(bctbx_map_t *map,bctbx_pair_t *pair); BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_cchar_insert_and_delete_with_returned_it(bctbx_map_t *map,bctbx_pair_t *pair); /*at return, it point to the next element*/ #define bctbx_map_erase bctbx_map_ullong_erase BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_ullong_erase(bctbx_map_t *map,bctbx_iterator_t *it); BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_cchar_erase(bctbx_map_t *map,bctbx_iterator_t *it); /*return a new allocated iterator*/ #define bctbx_map_begin bctbx_map_ullong_begin BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_ullong_begin(const bctbx_map_t *map); BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_cchar_begin(const bctbx_map_t *map); /*return a new allocated iterator*/ #define bctbx_map_end bctbx_map_ullong_end BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_ullong_end(const bctbx_map_t *map); BCTBX_PUBLIC bctbx_iterator_t *bctbx_map_cchar_end(const bctbx_map_t *map); /*return a new allocated iterator or null*/ #define bctbx_map_find_custom bctbx_map_ullong_find_custom BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_ullong_find_custom(const bctbx_map_t *map, bctbx_compare_func compare_func, const void *user_data); BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_cchar_find_custom(const bctbx_map_t *map, bctbx_compare_func compare_func, const void *user_data); /*return the iterator associated to the key in the map or Null*/ BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_ullong_find_key(const bctbx_map_t *map, unsigned long long key); BCTBX_PUBLIC bctbx_iterator_t * bctbx_map_cchar_find_key(const bctbx_map_t *map, const char * key); /* return the size of the map*/ #define bctbx_map_size bctbx_map_ullong_size BCTBX_PUBLIC size_t bctbx_map_ullong_size(const bctbx_map_t *map); BCTBX_PUBLIC size_t bctbx_map_cchar_size(const bctbx_map_t *map); /*iterator*/ #define bctbx_iterator_get_pair bctbx_iterator_ullong_get_pair BCTBX_PUBLIC bctbx_pair_t *bctbx_iterator_ullong_get_pair(const bctbx_iterator_t *it); BCTBX_PUBLIC bctbx_pair_t *bctbx_iterator_cchar_get_pair(const bctbx_iterator_t *it); /*return same pointer but pointing to next*/ #define bctbx_iterator_get_next bctbx_iterator_ullong_get_next BCTBX_PUBLIC bctbx_iterator_t *bctbx_iterator_ullong_get_next(bctbx_iterator_t *it); BCTBX_PUBLIC bctbx_iterator_t *bctbx_iterator_cchar_get_next(bctbx_iterator_t *it); #define bctbx_iterator_equals bctbx_iterator_ullong_equals BCTBX_PUBLIC bool_t bctbx_iterator_ullong_equals(const bctbx_iterator_t *a,const bctbx_iterator_t *b); BCTBX_PUBLIC bool_t bctbx_iterator_cchar_equals(const bctbx_iterator_t *a,const bctbx_iterator_t *b); #define bctbx_iterator_delete bctbx_iterator_ullong_delete BCTBX_PUBLIC void bctbx_iterator_ullong_delete(bctbx_iterator_t *it); BCTBX_PUBLIC void bctbx_iterator_cchar_delete(bctbx_iterator_t *it); /*pair*/ typedef struct _bctbx_pair_ullong_t bctbx_pair_ullong_t; /*inherit from bctbx_pair_t*/ BCTBX_PUBLIC bctbx_pair_ullong_t * bctbx_pair_ullong_new(unsigned long long key,void *value); typedef struct _bctbx_pair_cchar_t bctbx_pair_cchar_t; /*inherit from bctbx_pair_t*/ BCTBX_PUBLIC bctbx_pair_cchar_t * bctbx_pair_cchar_new(const char * key,void *value); #define bctbx_pair_get_second bctbx_pair_ullong_get_second BCTBX_PUBLIC void* bctbx_pair_ullong_get_second(const bctbx_pair_t * pair); BCTBX_PUBLIC void* bctbx_pair_cchar_get_second(const bctbx_pair_t * pair); BCTBX_PUBLIC unsigned long long bctbx_pair_ullong_get_first(const bctbx_pair_ullong_t * pair); BCTBX_PUBLIC const char * bctbx_pair_cchar_get_first(const bctbx_pair_cchar_t * pair); #define bctbx_pair_delete bctbx_pair_ullong_delete BCTBX_PUBLIC void bctbx_pair_ullong_delete(bctbx_pair_t * pair); BCTBX_PUBLIC void bctbx_pair_cchar_delete(bctbx_pair_t * pair); #ifdef __cplusplus } #endif #endif /* BCTBX_LIST_H_ */ bctoolbox-5.2.0/include/bctoolbox/ownership.hh000066400000000000000000000066031434566643100215070ustar00rootroot00000000000000/** SPDX-License-Identifier: GPL-3.0-or-later * * Pointer wrappers for explicit ownership semantics. * Those are NOT smart pointers (they do not manage memory) * * They can all be constructed from raw pointers to interop with existing code, but the conversion is explicit to make * sure the intent is clear. */ #pragma once #include namespace ownership { /* A pointer that is NOT responsible for the destruction of its pointee. * * Same as `Borrowed` but mutable (non-const) */ template class BorrowedMut { T* mPointer; public: /* State explicitly that this pointer represents a mutable borrow */ explicit BorrowedMut(T* pointer) : mPointer(pointer) { } operator T*() { return mPointer; }; }; /* A pointer that IS responsible for the destruction of its pointee. * * When reference counting, having an Owned means the reference count has been incremented. * * Use .take() to move the pointer out of the wrapper (typically before deleting the pointee). * Use .borrow() to use mutating operations without incrementing the reference count. */ template class Owned { T* mPointer; void assertFreed() { if (mPointer != nullptr) { throw std::logic_error("Owned pointer lost. If you did free it, maybe you forgot to call .take() before"); } } public: Owned(std::nullptr_t null) : mPointer(null) { } /* State explicitly that this pointer owns its pointee */ explicit Owned(T* pointer) : mPointer(pointer) { } /* Avoid inadvertently creating an owned pointer from a borrowed pointer */ Owned(BorrowedMut borrowed) = delete; ~Owned() { assertFreed(); } // Move only semantics to ensure ownership is always transferred Owned(Owned&& other) : mPointer(other.take()) { } Owned& operator=(Owned&& other) { assertFreed(); mPointer = other.take(); return *this; } Owned(const Owned& other) = delete; Owned& operator=(const Owned& other) = delete; /* Move the raw pointer out of the wrapper (leaving nullptr in its place). * Use it to pass ownership to the appropriate destructor */ T* take() { auto moved = mPointer; mPointer = nullptr; return moved; } /* Take a mutable borrow. * Use it to call mutating operations that don't need ownership */ BorrowedMut borrow() { return BorrowedMut(mPointer); } /* Automatically degrade into const pointer because that is always safe */ operator const T*() const { return mPointer; }; }; /* A const pointer that is NOT responsible for the destruction of its pointee. * * Care must be taken to ensure appropiate lifetimes: Since the borrow is not responsible for the lifetime of the * pointee, the latter might be destroyed before the borrow is used, leaving it dangling. */ template class Borrowed { const T* mPointer; public: /* State explicitly that this pointer represents an immutable borrow */ explicit Borrowed(const T* pointer) : mPointer(pointer) { } Borrowed(const Owned& owned) : Borrowed(static_cast(owned)) { } Borrowed(const BorrowedMut& mutBorrow) : Borrowed(static_cast(mutBorrow)) { } operator const T*() const { return mPointer; }; }; template auto owned(T* pointer) { return Owned(pointer); } template auto borrowed(const T* pointer) { return Borrowed(pointer); } template auto borrowed_mut(T* pointer) { return BorrowedMut(pointer); } } // namespace ownership bctoolbox-5.2.0/include/bctoolbox/param_string.h000066400000000000000000000041161434566643100220040ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_PARAM_STRING_H_ #define BCTBX_PARAM_STRING_H_ #include "bctoolbox/port.h" // This file provides string manipulation utility when handling a parameter string of the from // "param1;param2=120;param3=true" /** * Parses a fmtp string such as "profile=0;level=10", finds the value matching * parameter param_name, and writes it into result. * If a parameter name is found multiple times, only the value of the last occurence is returned. * @param paramString the fmtp line (format parameters) * @param param_name the parameter to search for * @param result the value given for the parameter (if found) * @param result_len the size allocated to hold the result string * @return TRUE if the parameter was found, else FALSE. **/ BCTBX_PUBLIC bool_t bctbx_param_string_get_value(const char *paramString, const char *param_name, char *result, size_t result_len); /** * Parses a fmtp string such as "profile=0;level=10". If the value is "true" or "false", returns the corresponding boolean * @param paramString the fmtp line (format parameters) * @param param_name the parameter to search for * @return FALSE if parameter was not found, else TRUE if the parameter value was "true", FALSE if it was "false" **/ BCTBX_PUBLIC bool_t bctbx_param_string_get_bool_value(const char *paramString, const char *param_name); #endif /*BCTBX_PARAM_STRING_H_*/ bctoolbox-5.2.0/include/bctoolbox/parser.h000066400000000000000000000075571434566643100206260ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_PARSER_H_ #define BCTBX_PARSER_H_ #include "bctoolbox/port.h" #ifdef __cplusplus extern "C" { #endif /** * A 256 entries table where each entries defines if character corresponding to its index is allowed or not (value = 0) * for instance noescape_rules[':'] = 1 means that ':' should not be escaped */ typedef unsigned char bctbx_noescape_rules_t[256+1]; /*last entry (BCTBX_NOESCAPE_RULES_USER_INDEX) is reserved for user purpose. Might be usefull to set if array was initialed of not */ #define BCTBX_NOESCAPE_RULES_USER_INDEX (sizeof(bctbx_noescape_rules_t) -1) /** * Allocate a new string with unauthorized characters escaped (I.E replaced by % HEX HEX) if any. * sample: * bctbx_noescape_rules_t my_rules = {0}; nothing allowed * bctbx_noescape_rules_add_alfanums(my_rules); * char * my_escaped_string = bctbx_escape("François",my_rules); * expeted result my_escaped_string == Fran%c3%a7ois * @param buff NULL terminated input buffer. * @param noescape_rules bctbx_noescape_rules_t to apply for this input buff * @return a newly allocated null terminated string */ BCTBX_PUBLIC char* bctbx_escape(const char* buff, const bctbx_noescape_rules_t noescape_rules); /** * Add a list of allowed charaters to a noescape rule. * @param noescape_rules rule to be modified. * @param allowed string representing allowed char. Sample: ";-/" */ BCTBX_PUBLIC void bctbx_noescape_rules_add_list(bctbx_noescape_rules_t noescape_rules, const char *allowed); /** * Add a range of allowed charaters to noescape rule. bctbx_noescape_rules_add_range(noescape_rules, '0','9') is the same as bctbx_noescape_rules_add_list(noescape_rules,"0123456789") * @param noescape_rules rule to be modified. * @param first allowed char. * @param last allowed char. */ BCTBX_PUBLIC void bctbx_noescape_rules_add_range(bctbx_noescape_rules_t noescape_rules, char first, char last); /** * Add ['0'..'9'], ['a'..'z'] ['A'..'z'] to no escape rule. */ BCTBX_PUBLIC void bctbx_noescape_rules_add_alfanums(bctbx_noescape_rules_t noescape_rules); /** * Allocate a new string with escaped character (I.E % HEX HEX) replaced by unicode char. * @param buff NULL terminated input buffer. * @return a newly allocated null terminated string with unescated values. */ BCTBX_PUBLIC char* bctbx_unescaped_string(const char* buff); /** * Does the same as bctbx_unescaped_string() except it does it only for characters in unescape_rules * @param buff NULL terminated input buffer. * @param unescape_rules bctbx_noescape_rules_t characters to unescape, other will be kept escaped * @return a newly allocated null terminated string with unescated values. */ BCTBX_PUBLIC char* bctbx_unescaped_string_only_chars_in_rules(const char* buff, const bctbx_noescape_rules_t unescape_rules); /** *Convert a single input "a" into unscaped output if need. * a = "%c3%a7" into "ç" with return value = 3 * a = "A" into "A" with return value = 1 * @param a input value * @param out outbut buffer for conversion * @return number of byte wrote into out */ BCTBX_PUBLIC size_t bctbx_get_char (const char *a, char *out); #ifdef __cplusplus } #endif #endif /*BCTBX_PARSER_H_*/ bctoolbox-5.2.0/include/bctoolbox/port.h000066400000000000000000000765771434566643100203260ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ /* this file is responsible of the portability of the stack */ #ifndef BCTBX_PORT_H #define BCTBX_PORT_H #if !defined(_WIN32) && !defined(_WIN32_WCE) /********************************/ /* definitions for UNIX flavour */ /********************************/ #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include #include #include #if defined(_XOPEN_SOURCE_EXTENDED) || !defined(__hpux) #include #endif #include #include typedef int bctbx_socket_t; typedef pthread_t bctbx_thread_t; typedef pthread_mutex_t bctbx_mutex_t; typedef pthread_cond_t bctbx_cond_t; #ifdef __INTEL_COMPILER #pragma warning(disable : 111) // statement is unreachable #pragma warning(disable : 181) // argument is incompatible with corresponding format string conversion #pragma warning(disable : 188) // enumerated type mixed with another type #pragma warning(disable : 593) // variable "xxx" was set but never used #pragma warning(disable : 810) // conversion from "int" to "unsigned short" may lose significant bits #pragma warning(disable : 869) // parameter "xxx" was never referenced #pragma warning(disable : 981) // operands are evaluated in unspecified order #pragma warning(disable : 1418) // external function definition with no prior declaration #pragma warning(disable : 1419) // external declaration in primary source file #pragma warning(disable : 1469) // "cc" clobber ignored #endif #ifndef BCTBX_DEPRECATED #define BCTBX_DEPRECATED __attribute__ ((deprecated)) #endif #define BCTBX_PUBLIC #define BCTBX_INLINE inline #define BCTBX_EWOULDBLOCK EWOULDBLOCK #define BCTBX_EINPROGRESS EINPROGRESS #define BCTBX_ENETUNREACH ENETUNREACH #define BCTBX_EHOSTUNREACH EHOSTUNREACH #define BCTBX_ENOTCONN ENOTCONN #define BCTBX_EPROTOTYPE EPROTOTYPE /* Protocol wrong type for socket */ #ifdef __cplusplus extern "C" { #endif int __bctbx_thread_join(bctbx_thread_t thread, void **ptr); int __bctbx_thread_create(bctbx_thread_t *thread, pthread_attr_t *attr, void * (*routine)(void*), void *arg); unsigned long __bctbx_thread_self(void); #ifdef __cplusplus } #endif #define bctbx_thread_create __bctbx_thread_create #define bctbx_thread_join __bctbx_thread_join #define bctbx_thread_self __bctbx_thread_self #define bctbx_thread_exit pthread_exit #define bctbx_mutex_init pthread_mutex_init #define bctbx_mutex_lock pthread_mutex_lock #define bctbx_mutex_unlock pthread_mutex_unlock #define bctbx_mutex_destroy pthread_mutex_destroy #define bctbx_cond_init pthread_cond_init #define bctbx_cond_signal pthread_cond_signal #define bctbx_cond_broadcast pthread_cond_broadcast #define bctbx_cond_wait pthread_cond_wait #define bctbx_cond_destroy pthread_cond_destroy #define bctbx_inet_aton inet_aton #define SOCKET_OPTION_VALUE void * #define SOCKET_BUFFER void * #define getSocketError() strerror(errno) #define getSocketErrorCode() (errno) #define getSocketErrorWithCode(code) strerror(code) #define bctbx_gettimeofday(tv,tz) gettimeofday(tv,tz) #define bctbx_log10f(x) log10f(x) #else /*********************************/ /* definitions for WIN32 flavour */ /*********************************/ #include #define _CRT_RAND_S #include #include #include #include #ifdef _MSC_VER #include #endif #if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP) #define BCTBX_WINDOWS_DESKTOP 1 #elif defined(WINAPI_FAMILY_PARTITION) //WINAPI_PARTITION_PC_APP => UWP //WINAPI_PARTITION_DESKTOP => Win32 Only //WINAPI_PARTITION_PHONE_APP => Windows Phone Store //WINAPI_PARTITION_APP => UWP+Windows Phone Store // On UWP, we have : 1001 // On Desktop : we have : 1101 // Uncomment line below to check combination //#define BCTBX_CHECK_FAMILY #ifdef BCTBX_CHECK_FAMILY #if defined (WINAPI_PARTITION_PC_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PC_APP) #define FAMILY1 #endif #if defined (WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define FAMILY2 #endif #if defined (WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) #define FAMILY3 #endif #if defined (WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define FAMILY4 #endif #ifdef FAMILY1 #ifdef FAMILY2 #ifdef FAMILY3 #ifdef FAMILY4 #error 1111 #else #error 1110 #endif #else #ifdef FAMILY4 #error 1101 #else #error 1100 #endif #endif #else #ifdef FAMILY3 #ifdef FAMILY4 #error 1011 #else #error 1010 #endif #else #ifdef FAMILY4 #error 1001 #else #error 1000 #endif #endif #endif #else #ifdef FAMILY2 #ifdef FAMILY3 #ifdef FAMILY4 #error 0111 #else #error 0110 #endif #else #ifdef FAMILY4 #error 0101 #else #error 0100 #endif #endif #else #ifdef FAMILY3 #ifdef FAMILY4 #error 0011 #else #error 0010 #endif #else #ifdef FAMILY4 #error 0001 #else #error 0000 #endif #endif #endif #endif #endif #if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define BCTBX_WINDOWS_DESKTOP 1 #elif defined (WINAPI_PARTITION_PC_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PC_APP) #define BCTBX_WINDOWS_DESKTOP 1 #define BCTBX_WINDOWS_UWP 1 #elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) #define BCTBX_WINDOWS_PHONE 1 #elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define BCTBX_WINDOWS_UNIVERSAL 1 #endif #endif #ifndef BCTBX_DEPRECATED #define BCTBX_DEPRECATED __declspec(deprecated) #endif #if defined(_WIN32) || defined(_WIN32_WCE) #ifdef BCTBX_STATIC #define BCTBX_PUBLIC #define BCTBX_VAR_PUBLIC extern #else #ifdef BCTBX_EXPORTS #define BCTBX_PUBLIC __declspec(dllexport) #define BCTBX_VAR_PUBLIC extern __declspec(dllexport) #else #define BCTBX_PUBLIC __declspec(dllimport) #define BCTBX_VAR_PUBLIC __declspec(dllimport) #endif #endif #pragma push_macro("_WINSOCKAPI_") #ifndef _WINSOCKAPI_ #define _WINSOCKAPI_ #endif #define strtok_r strtok_s typedef unsigned __int64 uint64_t; typedef __int64 int64_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef int int32_t; typedef unsigned char uint8_t; typedef __int16 int16_t; typedef long ssize_t; #else #include /*provided by mingw32*/ #include #define BCTBX_PUBLIC #define BCTBX_VAR_PUBLIC extern BCTBX_PUBLIC char* strtok_r(char *str, const char *delim, char **nextp); #endif #define vsnprintf _vsnprintf typedef SOCKET bctbx_socket_t; #ifdef BCTBX_WINDOWS_DESKTOP typedef HANDLE bctbx_cond_t; typedef HANDLE bctbx_mutex_t; #else typedef CONDITION_VARIABLE bctbx_cond_t; typedef SRWLOCK bctbx_mutex_t; #endif typedef HANDLE bctbx_thread_t; #define bctbx_thread_create __bctbx_WIN_thread_create #define bctbx_thread_join __bctbx_WIN_thread_join #define bctbx_thread_self __bctbx_WIN_thread_self #define bctbx_thread_exit(arg) #define bctbx_mutex_init __bctbx_WIN_mutex_init #define bctbx_mutex_lock __bctbx_WIN_mutex_lock #define bctbx_mutex_unlock __bctbx_WIN_mutex_unlock #define bctbx_mutex_destroy __bctbx_WIN_mutex_destroy #define bctbx_cond_init __bctbx_WIN_cond_init #define bctbx_cond_signal __bctbx_WIN_cond_signal #define bctbx_cond_broadcast __bctbx_WIN_cond_broadcast #define bctbx_cond_wait __bctbx_WIN_cond_wait #define bctbx_cond_destroy __bctbx_WIN_cond_destroy #define bctbx_inet_aton __bctbx_WIN_inet_aton #ifdef __cplusplus extern "C" { #endif BCTBX_PUBLIC int __bctbx_WIN_mutex_init(bctbx_mutex_t *m, void *attr_unused); BCTBX_PUBLIC int __bctbx_WIN_mutex_lock(bctbx_mutex_t *mutex); BCTBX_PUBLIC int __bctbx_WIN_mutex_unlock(bctbx_mutex_t *mutex); BCTBX_PUBLIC int __bctbx_WIN_mutex_destroy(bctbx_mutex_t *mutex); BCTBX_PUBLIC int __bctbx_WIN_thread_create(bctbx_thread_t *t, void *attr_unused, void *(*func)(void*), void *arg); BCTBX_PUBLIC int __bctbx_WIN_thread_join(bctbx_thread_t thread, void **unused); BCTBX_PUBLIC unsigned long __bctbx_WIN_thread_self(void); BCTBX_PUBLIC int __bctbx_WIN_cond_init(bctbx_cond_t *cond, void *attr_unused); BCTBX_PUBLIC int __bctbx_WIN_cond_wait(bctbx_cond_t * cond, bctbx_mutex_t * mutex); BCTBX_PUBLIC int __bctbx_WIN_cond_signal(bctbx_cond_t * cond); BCTBX_PUBLIC int __bctbx_WIN_cond_broadcast(bctbx_cond_t * cond); BCTBX_PUBLIC int __bctbx_WIN_cond_destroy(bctbx_cond_t * cond); BCTBX_PUBLIC int __bctbx_WIN_inet_aton (const char * cp, struct in_addr * addr); #ifdef __cplusplus } #endif #define SOCKET_OPTION_VALUE char * #define BCTBX_INLINE __inline #define BCTBX_EWOULDBLOCK WSAEWOULDBLOCK #define BCTBX_EINPROGRESS WSAEINPROGRESS #define BCTBX_ENETUNREACH WSAENETUNREACH #define BCTBX_EHOSTUNREACH WSAEHOSTUNREACH #define BCTBX_ENOTCONN WSAENOTCONN #define BCTBX_EPROTOTYPE WSAEPROTOTYPE /* Protocol wrong type for socket */ #if defined(_WIN32_WCE) #define bctbx_log10f(x) (float)log10 ((double)x) #ifdef assert #undef assert #endif /*assert*/ #define assert(exp) ((void)0) #ifdef errno #undef errno #endif /*errno*/ #define errno GetLastError() #ifdef strerror #undef strerror #endif /*strerror*/ const char * bctbx_strerror(DWORD value); #define strerror bctbx_strerror #else /*_WIN32_WCE*/ #define bctbx_log10f(x) log10f(x) #endif #ifdef __cplusplus extern "C" { #endif BCTBX_PUBLIC const char *__bctbx_getWinSocketError(int error); #ifdef __cplusplus } #endif #ifndef getSocketErrorCode #define getSocketErrorCode() WSAGetLastError() #endif #ifndef getSocketError #define getSocketError() __bctbx_getWinSocketError(WSAGetLastError()) #endif #ifndef getSocketErrorWithCode #define getSocketErrorWithCode(code) __bctbx_getWinSocketError(code) #endif #ifndef snprintf #define snprintf _snprintf #endif #ifndef strcasecmp #define strcasecmp _stricmp #endif #ifndef strncasecmp #define strncasecmp _strnicmp #endif #ifndef strdup #define strdup _strdup #endif #ifndef unlink #define unlink _unlink #endif #ifndef F_OK #define F_OK 00 /* Visual Studio does not define F_OK */ #endif #ifdef __cplusplus extern "C"{ #endif BCTBX_PUBLIC int bctbx_gettimeofday (struct timeval *tv, void* tz); #ifdef _WORKAROUND_MINGW32_BUGS char * WSAAPI gai_strerror(int errnum); #endif #ifdef __cplusplus } #endif #endif #ifndef _BOOL_T_ #define _BOOL_T_ typedef unsigned char bool_t; #endif /* _BOOL_T_ */ #undef TRUE #undef FALSE #define TRUE 1 #define FALSE 0 #ifndef MIN #define MIN(a,b) (((a)>(b)) ? (b) : (a)) #endif #ifndef MAX #define MAX(a,b) (((a)>(b)) ? (a) : (b)) #endif typedef struct bctoolboxTimeSpec{ int64_t tv_sec; int64_t tv_nsec; }bctoolboxTimeSpec; #ifdef __cplusplus extern "C"{ #endif BCTBX_PUBLIC void* bctbx_malloc(size_t sz); BCTBX_PUBLIC void bctbx_free(void *ptr); BCTBX_PUBLIC void* bctbx_realloc(void *ptr, size_t sz); BCTBX_PUBLIC void* bctbx_malloc0(size_t sz); BCTBX_PUBLIC char * bctbx_strdup(const char *tmp); /*override the allocator with this method, to be called BEFORE bctbx_init()*/ typedef struct _BctoolboxMemoryFunctions{ void *(*malloc_fun)(size_t sz); void *(*realloc_fun)(void *ptr, size_t sz); void (*free_fun)(void *ptr); }BctoolboxMemoryFunctions; void bctbx_set_memory_functions(BctoolboxMemoryFunctions *functions); #define bctbx_new(type,count) (type*)bctbx_malloc(sizeof(type)*(count)) #define bctbx_new0(type,count) (type*)bctbx_malloc0(sizeof(type)*(count)) BCTBX_PUBLIC int bctbx_socket_set_non_blocking(bctbx_socket_t sock); #ifdef __GNUC__ # define BCTBX_PRINTF_LIKE(format_pos, args_pos) __attribute__ ((format (printf, format_pos, args_pos))) #else # define BCTBX_PRINTF_LIKE(format_pos, args_pos) #endif BCTBX_PUBLIC char *bctbx_strndup(const char *str,int n); BCTBX_PUBLIC char *bctbx_strdup_printf(const char *fmt,...) BCTBX_PRINTF_LIKE(1, 2); BCTBX_PUBLIC char *bctbx_strdup_vprintf(const char *fmt, va_list ap); BCTBX_PUBLIC char *bctbx_strcat_printf(char *dst, const char *fmt,...) BCTBX_PRINTF_LIKE(2, 3); BCTBX_PUBLIC char *bctbx_strcat_vprintf(char *dst, const char *fmt, va_list ap); BCTBX_PUBLIC char *bctbx_concat(const char *str, ...); BCTBX_PUBLIC char *bctbx_replace(char *str, char c, char n); /** * @brief Return the directory part of a file path. * * Find the last delimiting character ('/' or '\') and return a * copy of the substring before. If no delimiter has been found, * then "." string is returned. * * @param[in] path A string containing a file path. The behaviour * is undefined if the string contains a path to a directoy. MUST be non-NULL. * @return An allocated string containing the directory name. * * @warning This funciton isn't equivalent to libgen.h dirname(). */ BCTBX_PUBLIC char *bctbx_dirname(const char *path); /** * @brief Return the name of a file from its path. * * Find the last delimiting character ('/' or '\') and return a * copy of the substring after. If no delimiter has been found, * then a copy of 'path' is returned. * * @param[in] path A string containing a file path. The behaviour * is undefined if the string contains a path to a directoy. MUST be non-NULL. * @return An allocated string containing the file name. * * @warning This funciton isn't equivalent to libgen.h basename(). */ BCTBX_PUBLIC char *bctbx_basename(const char *path); /** * Tests if a file with given path exists. * * @param[in] path the file path to test * @return 0 if yes, -1 otherwise. **/ BCTBX_PUBLIC int bctbx_file_exist(const char *pathname); /** * Tests if a directory with given pathname exists. * * @param[in] pathname the directory path to test * @return TRUE if yes, FALSE otherwise. **/ BCTBX_PUBLIC bool_t bctbx_directory_exists(const char *pathname); /* we just need to be able to return a pointer of type bctbx_list_t, the actual structure is defined in list.h */ struct _bctbx_list; /** * Parse a directory and return a list of all its files and subdirectories * * @param[in] path The directory to parse * @param[in] file_type select only files with that extension, can be NULL in that case it returns all files. * * @note . and .. file descriptors are never returned in the list, even when file_type is set to NULL * The returned list includes the given path in each item * * @return the list of all files in the given directory matching the given file type. * The list must be destroyed using bctbx_list_free_with_data(, bctbx_free) **/ BCTBX_PUBLIC struct _bctbx_list *bctbx_parse_directory(const char *path, const char *file_type); /** * Create a directory * Note: parent directory must exists, this function cannot create a complete path * * @param[in] path the directory to create * * @return 0 on success **/ BCTBX_PUBLIC int bctbx_mkdir(const char *path); /** * Delete a directory * * @param[in] path the directory to delete * @param[in] recursive if false, the directory must be empty to be deleted otherwise recursively delete with all content * * @return 0 on success **/ BCTBX_PUBLIC int bctbx_rmdir(const char *path, bool_t recursive); /** * @brief return a timeSpec structure(sec and nsec) containing current time(WARNING: there is no guarantees it is UTC ). * The time returned may refers to UTC or last boot. * Use this function only to compute a time span between two calls * @param[in/out] ret The current time (seconds and nano seconds). */ BCTBX_PUBLIC void bctbx_get_cur_time(bctoolboxTimeSpec *ret); /** * @brief return a timeSpec structure(sec and nsec) containing current UTC time. * * @param[in/out] ret The current UTC time, (seconds and nano seconds) */ BCTBX_PUBLIC void bctbx_get_utc_cur_time(bctoolboxTimeSpec *ret); BCTBX_PUBLIC uint64_t bctbx_get_cur_time_ms(void); BCTBX_PUBLIC void bctbx_sleep_ms(int ms); BCTBX_PUBLIC void bctbx_sleep_until(const bctoolboxTimeSpec *ts); /** * @brief Compares two TimeSpec s1 and s2. * * @param[in] s1 First time spec * @param[in] s2 Second time spec * * @return a negative value if s1 is earlier than s2, 0 if they are equal, a positive value if s1 is later than s2 */ BCTBX_PUBLIC int bctbx_timespec_compare(const bctoolboxTimeSpec *s1, const bctoolboxTimeSpec *s2); /** * @brief Add given amount of seconds to a timeSpec structure * * @param[in/out] ts The timeSpec structure used as input, modified in output by increnting it according to second argument * @param[in] lap In seconds, number of seconds to modify the given timeSpec, can be negative(which may set the original timeSpec to 0) */ BCTBX_PUBLIC void bctbx_timespec_add(bctoolboxTimeSpec *ts, const int64_t lap); /** * @brief Parse a string into a number of seconds * Accepted suffixes are Y,M,W,d,h,m,s number is expected to be a base 10 integer, no suffix means seconds * notes: * - M suffix(month) is consired a 30 days period without any consideration of the current date, Y is always 365 days. * - You can combine suffixes in any order: 3Y6M is valid, 15d1M6h is valid too. * - Any unknown suffix is silently ignored and the value preceding it is discarded * - NULL or empty string('\0') in timeString are valid and return 0. * * @param[in] timeString a string formated like {[0-9]+[Y,M,W,d,h,m,s]?}*'\0' (must be null terminated) * @return described time period in seconds */ BCTBX_PUBLIC uint32_t bctbx_time_string_to_sec(const char *timeString); /** * Generate a non cryptographically usable alea * * @return a 32 bits unsigned random. DO NOT USE THIS RANDOM SOURCE FOR ANY CRYPTOGRAPHIC OPERATION */ BCTBX_PUBLIC unsigned int bctbx_random(void); BCTBX_PUBLIC ssize_t bctbx_send(bctbx_socket_t socket, const void *buffer, size_t length, int flags); BCTBX_PUBLIC ssize_t bctbx_sendto(bctbx_socket_t socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len); BCTBX_PUBLIC ssize_t bctbx_recv(bctbx_socket_t socket, void *buffer, size_t length, int flags); BCTBX_PUBLIC ssize_t bctbx_recvfrom(bctbx_socket_t socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len); BCTBX_PUBLIC ssize_t bctbx_read(int fd, void *buf, size_t nbytes); BCTBX_PUBLIC ssize_t bctbx_write(int fd, const void *buf, size_t nbytes); /* Portable and bug-less getaddrinfo */ BCTBX_PUBLIC int bctbx_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); BCTBX_PUBLIC void bctbx_freeaddrinfo(struct addrinfo *res); BCTBX_PUBLIC int bctbx_getnameinfo(const struct sockaddr *address, socklen_t address_len, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); BCTBX_PUBLIC int bctbx_addrinfo_to_ip_address(const struct addrinfo *ai, char *ip, size_t ip_size, int *port); BCTBX_PUBLIC int bctbx_addrinfo_to_printable_ip_address(const struct addrinfo *ai, char *printable_ip, size_t printable_ip_size); BCTBX_PUBLIC int bctbx_sockaddr_to_ip_address(const struct sockaddr *sa, socklen_t salen, char *ip, size_t ip_size, int *port); BCTBX_PUBLIC int bctbx_sockaddr_to_printable_ip_address(struct sockaddr *sa, socklen_t salen, char *printable_ip, size_t printable_ip_size); /** Sort a list of addrinfo with the following rules: -IPV6 including NAT64. -V4 MAPPED IPV6. -V4. **/ BCTBX_PUBLIC struct addrinfo *bctbx_addrinfo_sort(struct addrinfo *ai); /** * Convert a numeric ip address and port into an addrinfo, whose family will be as specified in the first argument. * If AF_INET6 is requested, the returned addrinfo will always be an IPv6 address, possibly V4MAPPED if the * ip address was a v4 address. * Passing AF_UNSPEC to this function leads to unspecified results. **/ BCTBX_PUBLIC struct addrinfo * bctbx_ip_address_to_addrinfo(int family, int socktype, const char *ipaddress, int port); /** * Convert a name or ip address and port into an addrinfo, whose family will be as specified in the first argument. * If AF_INET6 is requested, the returned addrinfo will always be an IPv6 address, possibly a V4MAPPED if the * ip address was a v4 address. * Passing AF_UNSPEC to this function leads to unspecified results. **/ BCTBX_PUBLIC struct addrinfo * bctbx_name_to_addrinfo(int family, int socktype, const char *name, int port); /** * This function will transform a V4 to V6 mapped address to a pure V4 and write it into result, or will just copy it otherwise. * The memory for v6 and result may be the same, in which case processing is done in place or no copy is done. * The pointer to result must have sufficient storage, typically a struct sockaddr_storage. **/ BCTBX_PUBLIC void bctbx_sockaddr_remove_v4_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len); /** * This function will transform a V6 NAT64 mapped address to a pure V4 and write it into result, or will just copy it otherwise. * The memory for v6 and result may be the same, in which case processing is done in place or no copy is done. * The pointer to result must have sufficient storage, typically a struct sockaddr_storage. **/ BCTBX_PUBLIC void bctbx_sockaddr_remove_nat64_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len); /** * This function will transform a V4 to V6 mapped address to a pure V4 and write it into result, or will just copy it otherwise. * The memory for v6 and result may be the same, in which case processing is done in place or no copy is done. * The pointer to result must have sufficient storage, typically a struct sockaddr_storage. **/ BCTBX_PUBLIC void bctbx_sockaddr_ipv6_to_ipv4(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len); /** * This function will transform any V4 address to a V4 mapped address and write it into result. * The pointer to result must have sufficient storage, typically a struct sockaddr_storage. **/ BCTBX_PUBLIC void bctbx_sockaddr_ipv4_to_ipv6(const struct sockaddr *v4, struct sockaddr *result, socklen_t *result_len); /** * Return TRUE if both families, ports and addresses are equals */ BCTBX_PUBLIC bool_t bctbx_sockaddr_equals(const struct sockaddr * sa, const struct sockaddr * sb) ; /** * Get the local IP address that is used to send data to a specific destination. * @param[in] type The address family of the socket to use. * @param[in] dest The destination address. * @param[in] port The destination port. * @param[out] result The local IP address that is used to send data to the destination. * @param[in] result_len The size of the result buffer. * @return 0 on success, a negative value on error. **/ BCTBX_PUBLIC int bctbx_get_local_ip_for(int type, const char *dest, int port, char *result, size_t result_len); /* portable named pipes and shared memory*/ #if !defined(_WIN32_WCE) #ifdef _WIN32 typedef HANDLE bctbx_pipe_t; #define BCTBX_PIPE_INVALID INVALID_HANDLE_VALUE #else typedef int bctbx_pipe_t; #define BCTBX_PIPE_INVALID (-1) #endif BCTBX_PUBLIC bctbx_pipe_t bctbx_server_pipe_create_by_path(const char *path); BCTBX_PUBLIC bctbx_pipe_t bctbx_server_pipe_create(const char *name); /* * warning: on win32 bctbx_server_pipe_accept_client() might return INVALID_HANDLE_VALUE without * any specific error, this happens when bctbx_server_pipe_close() is called on another pipe. * This pipe api is not thread-safe. */ BCTBX_PUBLIC bctbx_pipe_t bctbx_server_pipe_accept_client(bctbx_pipe_t server); BCTBX_PUBLIC int bctbx_server_pipe_close(bctbx_pipe_t spipe); BCTBX_PUBLIC int bctbx_server_pipe_close_client(bctbx_pipe_t client); BCTBX_PUBLIC bctbx_pipe_t bctbx_client_pipe_connect(const char *name); BCTBX_PUBLIC int bctbx_client_pipe_close(bctbx_pipe_t sock); BCTBX_PUBLIC int bctbx_pipe_read(bctbx_pipe_t p, uint8_t *buf, int len); BCTBX_PUBLIC int bctbx_pipe_write(bctbx_pipe_t p, const uint8_t *buf, int len); BCTBX_PUBLIC void *bctbx_shm_open(unsigned int keyid, int size, int create); BCTBX_PUBLIC void bctbx_shm_close(void *memory); BCTBX_PUBLIC bool_t bctbx_is_multicast_addr(const struct sockaddr *addr); #endif /** * @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] input_char a char which shall be in range [0-9a-fA-F] * * @return the unsigned integer value in range [0-15] */ BCTBX_PUBLIC uint8_t bctbx_char_to_byte(uint8_t input_char); /** * @brief convert a byte which value is in range [0-15] into an hexa char [0-9a-fA-F] * * @param[in] input_byte an integer which shall be in range [0-15] * @return the hexa char [0-9a-f] corresponding to the input */ BCTBX_PUBLIC uint8_t bctbx_byte_to_char(uint8_t input_byte); /** * @brief Convert an hexadecimal string into the corresponding byte buffer * * @param[out] output_bytes The output bytes buffer, must have a length of half the input string buffer * @param[in] input_string The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0) * @param[in] input_string_length The lenght in chars of the string buffer, output is half this length */ BCTBX_PUBLIC void bctbx_str_to_uint8(uint8_t *output_bytes, const uint8_t *input_string, size_t input_string_length); /** * @brief Convert a byte buffer into the corresponding hexadecimal string * * @param[out] output_string The output string buffer, must have a length of twice the input bytes buffer * @param[in] input_bytes The input bytes buffer * @param[in] input_bytes_length The lenght in bytes buffer, output is twice this length */ BCTBX_PUBLIC void bctbx_int8_to_str(uint8_t *output_string, const uint8_t *input_bytes, size_t input_bytes_length); /** * @brief Convert an unsigned 32 bits integer into the corresponding hexadecimal string(including null termination character) * * @param[out] output_string The output string buffer, must have a length of at least 9 bytes(8 nibbles and the '\0') * @param[in] input_uint32 The input unsigned int */ BCTBX_PUBLIC void bctbx_uint32_to_str(uint8_t output_string[9], uint32_t input_uint32); /** * @brief Convert an hexadecimal string of 8 char length into the corresponding 32 bits unsigned integer * * @param[in] input_string The input string buffer, must be hexadecimal and at least 8 char long * * Note : there is no check on the length or validity as an hexa string on the input, incorrect byte is silently mapped to 0 */ BCTBX_PUBLIC uint32_t bctbx_str_to_uint32(const uint8_t *input_string); /** * @brief Convert an unsigned 64 bits integer into the corresponding hexadecimal string(including null termination character) * * @param[out] output_string The output string buffer, must have a length of at least 17 bytes(16 nibbles and the '\0') * @param[in] input_uint64 The input unsigned int */ BCTBX_PUBLIC void bctbx_uint64_to_str(uint8_t output_string[17], uint64_t input_uint64); /** * @brief Convert an hexadecimal string of 8 char length into the corresponding 64 bits unsigned integer * * @param[in] input_string The input string buffer, must be hexadecimal and at leat 16 char long * * Note : there is no check on the length or validity as an hexa string on the input, incorrect byte is silently mapped to 0 */ BCTBX_PUBLIC uint64_t bctbx_str_to_uint64(const uint8_t input_string[17]); /** * @brief Call of strcasecmp where parameters can be NULL. * * @param[in] a first string to compare. @maybenil * @param[in] b second string to compare. @maybenil * *@return the value of strcasecmp. */ BCTBX_PUBLIC int bctbx_strcasecmp(const char *a, const char *b); /** * @brief Call of strcmp where parameters can be NULL. * * @param[in] a first string to compare. @maybenil * @param[in] b second string to compare. @maybenil * *@return the value of strcmp. */ BCTBX_PUBLIC int bctbx_strcmp(const char *a, const char *b); /** * @brief Set the name of the calling thread. * This can ease the analysis of bug reports, such as ANR reports on Android. * @param[in] the name. Its length is subject to underlying platform's limitations. */ BCTBX_PUBLIC void bctbx_set_self_thread_name(const char *name); /** * @brief Set handlers to catch exceptions and write the stack trace into log. Available for Windows. * It keeps old handlers. * @param[in] if TRUE, prepends handlers with bctoolbox hooks. If FALSE, remove them and replace by old hooks (stored from the last call of bctbx_set_stack_trace_hooks() ). By default, it is FALSE. */ BCTBX_PUBLIC void bctbx_set_stack_trace_hooks(bool_t use_bctbx_hooks); #ifdef __cplusplus } #endif #ifndef IN6_IS_ADDR_MULTICAST #define IN6_IS_ADDR_MULTICAST(i) (((uint8_t *) (i))[0] == 0xff) #endif #if defined(_WIN32) || defined(__QNX__) /* Mingw32 does not define AI_V4MAPPED, however it is supported starting from Windows Vista. QNX also does not define AI_V4MAPPED. */ # ifndef AI_V4MAPPED # define AI_V4MAPPED 0x00000800 # endif # ifndef AI_ALL # define AI_ALL 0x00000100 # endif # ifndef IPV6_V6ONLY # define IPV6_V6ONLY 27 # endif #endif #endif #if defined(_WIN32) || defined(__QNX__) #define FORMAT_SIZE_T "%Iu" #elif __APPLE__ #define FORMAT_SIZE_T "%lu" #else #define FORMAT_SIZE_T "%zu" #endif #if defined(__ANDROID__) int mblen(const char* s, size_t n); int wctomb(char *s, wchar_t wc); #endif bctoolbox-5.2.0/include/bctoolbox/regex.h000066400000000000000000000020721434566643100204270ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_REGEX_H #define BCTBX_REGEX_H #ifdef __cplusplus extern "C" { #endif BCTBX_PUBLIC bool_t bctbx_is_matching_regex(const char *entry, const char* regex); BCTBX_PUBLIC bool_t bctbx_is_matching_regex_log(const char *entry, const char* regex, bool_t show_log); #ifdef __cplusplus } #endif #endif /* BCTBX_REGEX_H */ bctoolbox-5.2.0/include/bctoolbox/tester.h000066400000000000000000000245161434566643100206320ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTOOLBOX_TESTER_H #define BCTOOLBOX_TESTER_H #include #include #include #include #include typedef void (*test_function_t)(void); /** Function used in all suites - it is invoked before all and each tests and also after each and all tests * @return 0 means success, otherwise it's an error **/ typedef int (*pre_post_function_t)(void); // typedef int (*test_suite_function_t)(const char *name); typedef struct { const char *name; test_function_t func; const char *tags[2]; } test_t; #define TEST_NO_TAG_AUTO_NAMED(func) \ { #func, func, { NULL, NULL } } #define TEST_NO_TAG(name, func) \ { name, func, { NULL, NULL } } #define TEST_ONE_TAG(name, func, tag) \ { name, func, { tag, NULL } } #define TEST_TWO_TAGS(name, func, tag1, tag2) \ { name, func, { tag1, tag2 } } typedef struct { const char *name; /*suite name*/ pre_post_function_t before_all; /*function invoked before running the suite. If not returning 0, suite is not launched. */ pre_post_function_t after_all; /*function invoked at the end of the suite, even if some tests failed. */ test_function_t before_each; /*function invoked before each test within this suite. */ test_function_t after_each; /*function invoked after each test within this suite, even if it failed. */ int nb_tests; /* number of tests */ test_t *tests; /* tests within this suite */ } test_suite_t; #ifdef __cplusplus extern "C" { #endif #define CHECK_ARG(argument, index, argc) \ if(index >= argc) { \ fprintf(stderr, "Missing argument for \"%s\"\n", argument); \ return -1; \ } \ BCTBX_PUBLIC void bc_tester_init(void (*ftester_printf)(int level, const char *fmt, va_list args) , int verbosity_info, int verbosity_error, const char* expected_res); BCTBX_PUBLIC void bc_tester_helper(const char *name, const char* additionnal_helper); BCTBX_PUBLIC int bc_tester_parse_args(int argc, char** argv, int argid); BCTBX_PUBLIC int bc_tester_start(const char* prog_name); BCTBX_PUBLIC int bc_tester_register_suites(void); BCTBX_PUBLIC int bc_tester_register_suite_by_name(const char *suite_name); BCTBX_PUBLIC void bc_tester_add_suite(test_suite_t *suite); BCTBX_PUBLIC void bc_tester_set_max_parallel_suites(int max_parallel_suites); BCTBX_PUBLIC void bc_tester_set_max_failed_tests_threshold(int max_failed_tests_threshold); BCTBX_PUBLIC void bc_tester_set_global_timeout(int seconds); BCTBX_PUBLIC void bc_tester_uninit(void); BCTBX_PUBLIC void bc_tester_printf(int level, const char *fmt, ...); BCTBX_PUBLIC const char * bc_tester_get_resource_dir_prefix(void); BCTBX_PUBLIC void bc_tester_set_resource_dir_prefix(const char *name); BCTBX_PUBLIC const char * bc_tester_get_writable_dir_prefix(void); BCTBX_PUBLIC void bc_tester_set_writable_dir_prefix(const char *name); BCTBX_PUBLIC void bc_tester_set_silent_func(int (*func)(const char*)); BCTBX_PUBLIC void bc_tester_set_verbose_func(int (*func)(const char*)); BCTBX_PUBLIC void bc_tester_set_logfile_func(int (*func)(const char*)); BCTBX_PUBLIC void bc_tester_set_process_events_func(void (*func)(void)); BCTBX_PUBLIC void bc_tester_process_events(void); BCTBX_PUBLIC int bc_tester_nb_suites(void); BCTBX_PUBLIC int bc_tester_nb_tests(const char* name); BCTBX_PUBLIC void bc_tester_list_suites(void); BCTBX_PUBLIC void bc_tester_list_tests(const char *suite_name); BCTBX_PUBLIC const char * bc_tester_suite_name(int suite_index); BCTBX_PUBLIC const char * bc_tester_test_name(const char *suite_name, int test_index); BCTBX_PUBLIC int bc_tester_run_suite(test_suite_t *suite, const char *tag_name); BCTBX_PUBLIC int bc_tester_run_tests(const char *suite_name, const char *test_name, const char *tag_name); BCTBX_PUBLIC int bc_tester_suite_index(const char *suite_name); BCTBX_PUBLIC const char * bc_tester_current_suite_name(void); BCTBX_PUBLIC const char * bc_tester_current_test_name(void); BCTBX_PUBLIC const char ** bc_tester_current_test_tags(void); BCTBX_PUBLIC char* bc_sprintfva(const char* format, va_list args); BCTBX_PUBLIC char* bc_sprintf(const char* format, ...); BCTBX_PUBLIC void bc_free(void *ptr); BCTBX_PUBLIC char * bc_tester_get_failed_asserts(void); BCTBX_PUBLIC unsigned int bc_get_number_of_failures(void); BCTBX_PUBLIC void bc_set_trace_handler(void(*handler)(int, const char*, va_list)); /** * Get full path to the given resource * * @param name relative resource path * @return path to the resource. Must be freed by caller. */ BCTBX_PUBLIC char * bc_tester_res(const char *name); /** * Get full path to the given writable_file * * @param name relative writable file path * @return path to the writable file. Must be freed by caller. */ BCTBX_PUBLIC char * bc_tester_file(const char *name); /*Redefine the CU_... macros WITHOUT final ';' semicolon, to allow IF conditions and smarter error message */ extern int CU_assertImplementation(int bValue, unsigned int uiLine, const char *strCondition, const char *strFile, const char *strFunction, int bFatal); /** * Test unit assertion * * @return 1 if assert was true, 0 otherwise */ BCTBX_PUBLIC int bc_assert(const char* file, int line, int predicate, const char* format); #define _BC_ASSERT_PRED(name, pred, actual, expected, type, ...) \ do { \ char format[4096] = {0}; \ type cactual = (actual); \ type cexpected = (expected); \ snprintf(format, 4096, name "(" #actual ", " #expected ") - " __VA_ARGS__); \ bc_assert(__FILE__, __LINE__, pred, format); \ } while (0) #define BC_PASS(msg) bc_assert(__FILE__, __LINE__, TRUE, "BC_PASS(" #msg ").") #define BC_FAIL(msg) bc_assert(__FILE__, __LINE__, FALSE, "BC_FAIL(" #msg ").") #define BC_ASSERT(value) bc_assert(__FILE__, __LINE__, (value), #value) #define BC_TEST(value) bc_assert(__FILE__, __LINE__, (value), #value) #define BC_ASSERT_TRUE(value) bc_assert(__FILE__, __LINE__, (value), "BC_ASSERT_TRUE(" #value ")") #define BC_ASSERT_FALSE(value) bc_assert(__FILE__, __LINE__, !(value), "BC_ASSERT_FALSE(" #value ")") #define BC_ASSERT_PTR_EQUAL(actual, expected) bc_assert(__FILE__, __LINE__, ((actual) == (expected)), "BC_ASSERT_PTR_EQUAL(" #actual "!=" #expected ")") #define BC_ASSERT_PTR_NOT_EQUAL(actual, expected) bc_assert(__FILE__, __LINE__, ((actual) != (expected)), "BC_ASSERT_PTR_NOT_EQUAL(" #actual "==" #expected ")") #define BC_ASSERT_PTR_NULL(value) bc_assert(__FILE__, __LINE__, ((value) == NULL), "BC_ASSERT_PTR_NULL(" #value ")") #define BC_ASSERT_PTR_NOT_NULL(value) bc_assert(__FILE__, __LINE__, ((value) != NULL), "BC_ASSERT_PTR_NOT_NULL(" #value ")") #define BC_ASSERT_STRING_NOT_EQUAL(actual, expected) _BC_ASSERT_PRED("BC_ASSERT_STRING_NOT_EQUAL", (strcmp((const char*)(cactual), (const char*)(cexpected))), actual, expected, const char*, "Expected NOT %s but it was.", cexpected) #define BC_ASSERT_NSTRING_EQUAL(actual, expected, count) _BC_ASSERT_PRED("BC_ASSERT_NSTRING_EQUAL", !(strncmp((const char*)(cactual), (const char*)(cexpected), (size_t)(count))), actual, expected, const char*, "Expected %*s but was %*s.", (int)(count), cexpected, (int)(count), cactual) #define BC_ASSERT_NSTRING_NOT_EQUAL(actual, expected, count) _BC_ASSERT_PRED("BC_ASSERT_NSTRING_NOT_EQUAL", (strncmp((const char*)(cactual), (const char*)(cexpected), (size_t)(count))), actual, expected, const char*, "Expected %*s but it was.", (int)count, cexpected) #define BC_ASSERT_DOUBLE_EQUAL(actual, expected, granularity) _BC_ASSERT_PRED("BC_ASSERT_DOUBLE_EQUAL", ((fabs((double)(cactual) - (cexpected)) <= fabs((double)(granularity)))), actual, expected, double, "Expected %f but was %f.", cexpected, cactual) #define BC_ASSERT_DOUBLE_NOT_EQUAL(actual, expected, granularity) _BC_ASSERT_PRED("BC_ASSERT_DOUBLE_NOT_EQUAL", ((fabs((double)(cactual) - (cexpected)) > fabs((double)(granularity)))), actual, expected, double, "Expected %f but was %f.", cexpected, cactual) #define BC_ASSERT_EQUAL(actual, expected, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_EQUAL", ((cactual) == (cexpected)), actual, expected, type, "Expected " type_format " but was " type_format ".", cexpected, cactual) #define BC_ASSERT_NOT_EQUAL(actual, expected, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_NOT_EQUAL", ((cactual) != (cexpected)), actual, expected, type, "Expected NOT " type_format " but it was.", cexpected) #define BC_ASSERT_STRING_EQUAL(actual, expected) _BC_ASSERT_PRED("BC_ASSERT_STRING_EQUAL", cactual && cexpected && !(strcmp((const char*)(cactual), (const char*)(cexpected))), actual, expected, const char*, "Expected %s but was %s.", cexpected, cactual) #define BC_ASSERT_GREATER(actual, min, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_GREATER", ((cactual) >= (cexpected)), actual, min, type, "Expected at least " type_format " but was " type_format ".", cexpected, cactual) #define BC_ASSERT_LOWER(actual, max, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_LOWER", ((cactual) <= (cexpected)), actual, max, type, "Expected at most " type_format " but was " type_format ".", cexpected, cactual) #define BC_ASSERT_GREATER_STRICT(actual, min, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_GREATER", ((cactual) > (cexpected)), actual, min, type, "Expected more than " type_format " but was " type_format ".", cexpected, cactual) #define BC_ASSERT_LOWER_STRICT(actual, max, type, type_format) _BC_ASSERT_PRED("BC_ASSERT_LOWER", ((cactual) < (cexpected)), actual, max, type, "Expected less than " type_format " but was " type_format ".", cexpected, cactual) #ifdef __cplusplus } #endif #endif bctoolbox-5.2.0/include/bctoolbox/utils.hh000066400000000000000000000043141434566643100206260ustar00rootroot00000000000000/* * Copyright (c) 2016-2021 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_UTILS_H #define BCTBX_UTILS_H #include #include #include #include "bctoolbox/port.h" namespace bctoolbox { namespace Utils { BCTBX_PUBLIC std::vector split (const std::string &str, const std::string &delimiter); BCTBX_PUBLIC inline std::vector split (const std::string &str, char delimiter) { return split(str, std::string(1, delimiter)); } template inline const T &getEmptyConstRefObject () { static const T object{}; return object; } BCTBX_PUBLIC std::string fold (const std::string &str); BCTBX_PUBLIC std::string unfold (const std::string &str); // Replace all "from" by "to" in source. Use 'recursive' to avoid replacing what has been replaced. BCTBX_PUBLIC void replace(std::string& source, const std::string& from, const std::string& to, const bool& recursive = true); // Return the current state of memory as a string. This is currently implemented only for Windows. BCTBX_PUBLIC std::string getMemoryReportAsString(); // Replace const_cast in order to be adapted from types. Be carefull when using it. template class auto_cast { public: explicit constexpr auto_cast(From const& t) noexcept : val { t }{} template constexpr operator To() const noexcept(noexcept(const_cast(std::declval()))) { return const_cast(val); } private: From const& val; }; } } #endif /* BCTBX_UTILS_H */ bctoolbox-5.2.0/include/bctoolbox/vconnect.h000066400000000000000000000165141434566643100211420ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VCONNECT #define BCTBX_VCONNECT #include #include #if !defined(_WIN32_WCE) #include #include #if _MSC_VER #include #endif #endif /*_WIN32_WCE*/ #ifndef _WIN32 #include #endif #define BCTBX_VCONNECT_OK 0 /* Successful result */ #define BCTBX_VCONNECT_ERROR -255 /* Some kind of socket error occurred */ #ifdef __cplusplus extern "C"{ #endif /** * Methods associated with the bctbx_vsocket_api_t. */ typedef struct bctbx_vsocket_methods_t bctbx_vsocket_methods_t; /** * Methods pointer prototypes for the socket functions. */ struct bctbx_vsocket_methods_t { bctbx_socket_t (*pFuncSocket)(int socket_family, int socket_type, int protocol); int (*pFuncConnect)(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len); int (*pFuncBind)(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len); int (*pFuncGetSockName)(bctbx_socket_t sockfd, struct sockaddr *addr, socklen_t *addrlen); int (*pFuncGetSockOpt)(bctbx_socket_t sockfd, int level, int optname, void *optval, socklen_t *optlen); int (*pFuncSetSockOpt)(bctbx_socket_t sockfd, int level, int optname, const void *optval, socklen_t optlen); int (*pFuncClose)(bctbx_socket_t sock); char* (*pFuncGetError)(int err); int (*pFuncShutdown)(bctbx_socket_t sock, int how); }; /** * Socket API structure definition */ typedef struct bctbx_vsocket_api_t bctbx_vsocket_api_t; struct bctbx_vsocket_api_t { const char *vSockName; /* Socket API name */ const bctbx_vsocket_methods_t *pSocketMethods; /* Pointer to the socket methods structure */ }; /**=================================================== * Socket API methods prototypes. * The default implementation relies on libc methods. *====================================================*/ /** * Creates a socket. * @param socket_family type of address structure * @param socket_type socket type i.e SOCK_STREAM * @param protocol protocol family i.e AF_INET * @return Returns a socket file descriptor. */ BCTBX_PUBLIC bctbx_socket_t bctbx_socket(int socket_family, int socket_type, int protocol); /** * Get the local socket address. * Calls the function pointer pFuncGetSockName. The default method associated with this pointer * is getsockname. * @param sockfd socket descriptor * @param addr buffer holding the current address to which the socket sockfd is bound * @param addrlen amount of space (in bytes) pointed to by addr * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_getsockname(bctbx_socket_t sockfd, struct sockaddr *addr, socklen_t *addrlen); /** * Get socket options. * @param sockfd socket file descriptor * @param level level of the socket option * @param optname name of the option * @param optval buffer in which the value for the requested option(s) are to be returned * @param optlen contains the size of the buffer pointed to by optval, and on return * contains the actual size of the value returned. * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_getsockopt(bctbx_socket_t sockfd, int level, int optname, void *optval, socklen_t *optlen); /** * Set socket options. * @param sockfd socket file descriptor * @param level level of the socket option * @param optname name of the option * @param optval buffer holding the value for the requested option * @param optlen contains the size of the buffer pointed to by optval * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_setsockopt(bctbx_socket_t sockfd, int level, int optname, const void *optval, socklen_t optlen); /** * Shut down part of a full duplex connection. * @param sockfd socket file descriptor * @param how specifies what is shut down in the connection. * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_shutdown(bctbx_socket_t sockfd, int how); /** * Close a socket file descriptor. * @param sockfd socket file descriptor * @return 0 on success , -1 on error and errno is set. */ BCTBX_PUBLIC int bctbx_socket_close(bctbx_socket_t sockfd); /** * Assign a name to a socket. * @param sockfd socket file descriptor to assign the name to. * @param address address of the socket * @param address_len size of the address structure pointed to by address (bytes) * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_bind(bctbx_socket_t sockfd, const struct sockaddr *address, socklen_t address_len); /** * Initialize a connection to a socket. * @param sockfd socket file descriptor * @param address address of the socket * @param address_len size of the address structure pointed to by address (bytes) * @return On success, zero is returned. On error, -1 is returned, and errno is * set appropriately. */ BCTBX_PUBLIC int bctbx_connect(bctbx_socket_t sockfd, const struct sockaddr *address, socklen_t address_len); /** * strerror equivalent. * When an error is returned on a socket operations, returns * the error description based on err (errno) value. * @param err should be set to errno * @return Error description */ BCTBX_PUBLIC char* bctbx_socket_error(int err); /** * Set default bctbx_vsocket_api_t pointer pDefaultvSocket to my_vsocket_api * if it is not NULL, sets it to the standard API implementation otherwise. * By default, the global pointer is set to use bcvSocket implemented in vconnect.c * @param my_vsocket_api Pointer to a bctbx_vsocket_api_t structure. */ BCTBX_PUBLIC void bctbx_vsocket_api_set_default(bctbx_vsocket_api_t *my_vsocket_api); /** * Returns the value of the global variable pDefaultvSocket, * pointing to the default bctbx_vsocket_api_t used. * @return Pointer to bctbx_vsocket_api_t set to operate as default. */ BCTBX_PUBLIC bctbx_vsocket_api_t* bctbx_vsocket_api_get_default(void); /** * Return pointer to standard bctbx_vsocket_api_t implementation based on libc * functions. * @return pointer to bcSocketAPI */ BCTBX_PUBLIC bctbx_vsocket_api_t* bctbx_vsocket_api_get_standard(void); #ifdef __cplusplus } #endif #endif /* BCTBX_VCONNECT */ bctoolbox-5.2.0/include/bctoolbox/vfs.h000066400000000000000000000234171434566643100201210ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VFS_H #define BCTBX_VFS_H #include #include #if !defined(_WIN32_WCE) #include #include #if _MSC_VER #include #endif #endif /*_WIN32_WCE*/ #ifndef _WIN32 #include #endif #ifdef _WIN32 #ifndef S_IRUSR #define S_IRUSR S_IREAD #endif #ifndef S_IWUSR #define S_IWUSR S_IWRITE #endif #endif /*!_WIN32*/ #define BCTBX_VFS_OK 0 /* Successful result */ #define BCTBX_VFS_ERROR -255 /* Some kind of disk I/O error occurred */ #define BCTBX_VFS_PRINTF_PAGE_SIZE 4096 /* Size of the page hold in memory by fprintf */ #define BCTBX_VFS_GETLINE_PAGE_SIZE 17385 /* Size of the page hold in memory by getnextline */ #ifdef __cplusplus extern "C"{ #endif /** * Methods associated with the bctbx_vfs_t. */ typedef struct bctbx_io_methods_t bctbx_io_methods_t; /** * VFS file handle. */ typedef struct bctbx_vfs_file_t bctbx_vfs_file_t; struct bctbx_vfs_file_t { const struct bctbx_io_methods_t *pMethods; /* Methods for an open file: all Developpers must supply this field at open step*/ /*the fields below are used by the default implementation. Developpers are not required to supply them, but may use them if they find * them useful*/ void* pUserData; /* Developpers can store private data under this pointer */ off_t offset; /* File offset used by bctbx_file_fprintf and bctbx_file_get_nxtline */ /* fprintf cache */ char fPage[BCTBX_VFS_PRINTF_PAGE_SIZE]; /* Buffer storing the current page cached by fprintf */ off_t fPageOffset; /* The original offset of the cached page */ size_t fSize; /* number of bytes in cache */ /* get_nxtline cache */ char gPage[BCTBX_VFS_GETLINE_PAGE_SIZE+1]; /* Buffer storing the current page cachec by get_nxtline +1 to hold the \0 */ off_t gPageOffset; /* The offset of the cached page */ size_t gSize; /* actual size of the data in cache */ }; /** */ struct bctbx_io_methods_t { int (*pFuncClose)(bctbx_vfs_file_t *pFile); ssize_t (*pFuncRead)(bctbx_vfs_file_t *pFile, void* buf, size_t count, off_t offset); ssize_t (*pFuncWrite)(bctbx_vfs_file_t *pFile, const void* buf, size_t count, off_t offset); int (*pFuncTruncate)(bctbx_vfs_file_t *pFile, int64_t size); int64_t (*pFuncFileSize)(bctbx_vfs_file_t *pFile); int (*pFuncSync)(bctbx_vfs_file_t *pFile); int (*pFuncGetLineFromFd)(bctbx_vfs_file_t *pFile, char* s, int count); bool_t (*pFuncIsEncrypted)(bctbx_vfs_file_t *pFile); }; /** * VFS definition */ typedef struct bctbx_vfs_t bctbx_vfs_t; struct bctbx_vfs_t { const char *vfsName; /* Virtual file system name */ int (*pFuncOpen)(bctbx_vfs_t *pVfs, bctbx_vfs_file_t *pFile, const char *fName, int openFlags); }; /** * Attempts to read count bytes from the open file given by pFile, at the position starting at offset * in the file and and puts them in the buffer pointed by buf. * @param pFile bctbx_vfs_file_t File handle pointer. * @param buf Buffer holding the read bytes. * @param count Number of bytes to read. * @param offset Where to start reading in the file (in bytes). * @return Number of bytes read on success, BCTBX_VFS_ERROR otherwise. */ BCTBX_PUBLIC ssize_t bctbx_file_read(bctbx_vfs_file_t *pFile, void *buf, size_t count, off_t offset); /** * Attempts to read count bytes from the open file given by pFile, at the position starting at its offset * in the file and and puts them in the buffer pointed by buf. * The file offset shall be incremented by the number of bytes actually read. * @param pFile bctbx_vfs_file_t File handle pointer. * @param buf Buffer holding the read bytes. * @param count Number of bytes to read. * @return Number of bytes read on success, BCTBX_VFS_ERROR otherwise. */ BCTBX_PUBLIC ssize_t bctbx_file_read2(bctbx_vfs_file_t *pFile, void *buf, size_t count); /** * Close the file from its descriptor pointed by thw bctbx_vfs_file_t handle. * @param pFile File handle pointer. * @return return value from the pFuncClose VFS Close function on success, * BCTBX_VFS_ERROR otherwise. */ BCTBX_PUBLIC int bctbx_file_close(bctbx_vfs_file_t *pFile); /** * Allocates a bctbx_vfs_file_t file handle pointer. Opens the file fName * with the mode specified by the mode argument. Calls bctbx_file_open. * @param pVfs Pointer to the vfs instance in use. * @param fName Absolute file path. * @param mode File access mode (char*). * @return pointer to bctbx_vfs_file_t on success, NULL otherwise. */ BCTBX_PUBLIC bctbx_vfs_file_t* bctbx_file_open(bctbx_vfs_t *pVfs, const char *fName, const char *mode); /** * Allocates a bctbx_vfs_file_t file handle pointer. Opens the file fName * with the mode specified by the mode argument. Calls bctbx_file_open. * @param pVfs Pointer to the vfs instance in use. * @param fName Absolute file path. * @param openFlags File access flags(integer). * @return pointer to bctbx_vfs_file_t on success, NULL otherwise. */ BCTBX_PUBLIC bctbx_vfs_file_t* bctbx_file_open2(bctbx_vfs_t *pVfs, const char *fName, const int openFlags); /** * Returns the file size. * @param pFile bctbx_vfs_file_t File handle pointer. * @return BCTBX_VFS_ERROR if an error occured, file size otherwise. */ BCTBX_PUBLIC int64_t bctbx_file_size(bctbx_vfs_file_t *pFile); /** * Truncates/ Extends a file. * @param pFile bctbx_vfs_file_t File handle pointer. * @param size New size of the file. * @return BCTBX_VFS_ERROR if an error occured, 0 otherwise. */ BCTBX_PUBLIC int bctbx_file_truncate(bctbx_vfs_file_t *pFile, int64_t size); /** * Write count bytes contained in buf to a file associated with pFile at the position * offset. Calls pFuncWrite (set to bc_Write by default). * @param pFile File handle pointer. * @param buf Buffer hodling the values to write. * @param count Number of bytes to write to the file. * @param offset Position in the file where to start writing. * @return Number of bytes written on success, BCTBX_VFS_ERROR if an error occurred. */ BCTBX_PUBLIC ssize_t bctbx_file_write(bctbx_vfs_file_t *pFile, const void *buf, size_t count, off_t offset); /** * Write count bytes contained in buf to a file associated with pFile at the position starting at its * offset. Calls pFuncWrite (set to bc_Write by default). * The file offset shall be incremented by the number of bytes actually written. * @param pFile File handle pointer. * @param buf Buffer hodling the values to write. * @param count Number of bytes to write to the file. * @return Number of bytes written on success, BCTBX_VFS_ERROR if an error occurred. */ BCTBX_PUBLIC ssize_t bctbx_file_write2(bctbx_vfs_file_t *pFile, const void *buf, size_t count); /** * Writes to file. * @param pFile File handle pointer. * @param offset where to write in the file * @param fmt format argument, similar to that of printf * @return Number of bytes written if success, BCTBX_VFS_ERROR otherwise. */ BCTBX_PUBLIC ssize_t bctbx_file_fprintf(bctbx_vfs_file_t *pFile, off_t offset, const char *fmt, ...); /** * Wrapper to pFuncGetNxtLine. Returns a line with at most maxlen characters * from the file associated to pFile and writes it into s. * @param pFile File handle pointer. * @param s Buffer where to store the read line. * @param maxlen Number of characters to read to find a line in the file. * @return BCTBX_VFS_ERROR if an error occurred, size of line read otherwise. */ BCTBX_PUBLIC int bctbx_file_get_nxtline(bctbx_vfs_file_t *pFile, char *s, int maxlen); /** * Simply sync the file contents given through the file handle * to the persistent media. * @param pFile File handle pointer. * @return BCTBX_VFS_OK on success, BCTBX_VFS_ERROR otherwise */ BCTBX_PUBLIC int bctbx_file_sync(bctbx_vfs_file_t *pFile); /** * Set the position to offset in the file, this position is used only by the function * bctbx_file_get_nxtline. Read and write give their own offset as param and won't modify this one * @param pFile File handle pointer. * @param offset File offset where to set the position to. * @param whence Either SEEK_SET, SEEK_CUR,SEEK_END * @return BCTBX_VFS_ERROR on error, offset otherwise. */ BCTBX_PUBLIC off_t bctbx_file_seek(bctbx_vfs_file_t *pFile, off_t offset, int whence); /** * Get the file encryption status * @param pFile File handle pointer. * @return true if the file is encrypted */ BCTBX_PUBLIC bool_t bctbx_file_is_encrypted(bctbx_vfs_file_t *pFile); /** * Set default VFS pointer pDefault to my_vfs. * By default, the global pointer is set to use VFS implemnted in vfs.c * @param my_vfs Pointer to a bctbx_vfs_t structure. */ BCTBX_PUBLIC void bctbx_vfs_set_default(bctbx_vfs_t *my_vfs); /** * Returns the value of the global variable pDefault, * pointing to the default vfs used. * @return Pointer to bctbx_vfs_t set to operate as default VFS. */ BCTBX_PUBLIC bctbx_vfs_t* bctbx_vfs_get_default(void); /** * Return pointer to standard VFS impletentation. * @return pointer to bcVfs */ BCTBX_PUBLIC bctbx_vfs_t* bctbx_vfs_get_standard(void); #ifdef __cplusplus } #endif #endif /* BCTBX_VFS_H */ bctoolbox-5.2.0/include/bctoolbox/vfs_encrypted.hh000066400000000000000000000176561434566643100223560ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VFS_ENCRYPTED_HH #define BCTBX_VFS_ENCRYPTED_HH #include "bctoolbox/vfs.h" #include "bctoolbox/exception.hh" #include #include #include #include "bctoolbox/port.h" namespace bctoolbox { /** * @brief This dedicated exception inherits \ref BctoolboxException. * */ class EvfsException : public BctbxException { public: EvfsException() = default; EvfsException(const std::string &message): BctbxException(message) {} EvfsException(const char *message): BctbxException(message) {} virtual ~EvfsException() throw() {} EvfsException(const EvfsException &other): BctbxException(other) {} template EvfsException &operator<<(const T &val) { BctbxException::operator<<(val); return *this; } }; #define EVFS_EXCEPTION EvfsException() << " " << __FILE__ << ":" << __LINE__ << " " /** * Virtual File sytem provided */ extern BCTBX_PUBLIC bctbx_vfs_t bcEncryptedVfs; /** * Provided encryption suites */ enum class EncryptionSuite : uint16_t { unset = 0,/**< no encryption suite selected */ dummy = 1, /**< a test suite, do not use other than for test */ aes256gcm128_sha256 = 2, /**< This module encrypts blocks with AES256GCM and authenticate header using HMAC-sha256 */ plain = 0xFFFF /**< no encryption activated, direct use of standard file system API */ }; /** * Helper function returning a string holding the suite name * @param[in] suite the encryption suite as a c++ enum * @return the suite name in a readable string */ const std::string encryptionSuiteString(const EncryptionSuite suite) noexcept; /* complete declaration follows, we need this one to define the callback type */ class VfsEncryption; /** * Define a function prototype to be called at each file opening. * This function is a static class property, used to retrieve secretMaterial to encrypt/decrypt the file */ using EncryptedVfsOpenCb = std::function; // forward declare this type, store all the encryption data and functions class VfsEncryptionModule; /** Store in the bctbx_vfs_file_t userData field an object specific to encryption */ class VfsEncryption { /* Class properties and method */ private: static EncryptedVfsOpenCb s_openCallback; /**< a class callback to get secret material at file opening. Implemented as static as it is called by constructor */ public: /** * at file opening a callback ask for crypto material, it is class property, set it using this class method */ static void openCallbackSet(EncryptedVfsOpenCb cb) noexcept; static EncryptedVfsOpenCb openCallbackGet() noexcept; /* Object properties and methods */ private: uint16_t mVersionNumber; /**< version number of the encryption vfs */ size_t mChunkSize; /**< size of the file chunks payload in bytes : default is 4kB */ size_t rawChunkSizeGet() const noexcept; /** return the size of a chunk including its encryption header, as stored in the raw file */ std::shared_ptr m_module; /**< one of the available encryption module : if nullptr, assume we deal with regular plain file */ size_t mHeaderExtensionSize; /**< header extension size */ const std::string mFilename; /**< the filename as given to the open function */ uint64_t mFileSize; /**< size of the plaintext file */ uint64_t rawFileSizeGet() const noexcept; /**< return the size of the raw file */ uint32_t getChunkIndex(uint64_t offset) const noexcept; /**< return the chunk index where to find the given offset */ size_t getChunkOffset(uint32_t index) const noexcept; /**< return the offset in the actual file of the begining of the chunk */ std::vector r_header; /**< a cache of the header - without the encryption module data */ /** flags use to communicate during differents functions involved at file opening **/ bool mEncryptExistingPlainFile; /**< when opening a plain file, if the callback set an encryption suite and key material : migrate the file */ bool mIntegrityFullCheck; /**< if the file size given in the header metadata is incorrect, full check the file integrity and revrite header */ int mAccessMode; /**< the flags used to open the file, filtered on the access mode */ /** * Parse the header of an encrypted file, check everything seems correct * may perform integrity checking if the encryption module provides it * * @throw a EvfsException if something goes wrong */ void parseHeader(); /** * Write the encrypted file header to the actual file * Create the needed structures if the file is actually empty * @param[in] fp if a file pointer is given write to this one, otherwise use the pFileStp property * * @throw a EvfsException if something goes wrong **/ void writeHeader(bctbx_vfs_file_t *fp=nullptr); public: bctbx_vfs_file_t *pFileStd; /**< The encrypted vfs encapsulate a standard one */ VfsEncryption(bctbx_vfs_file_t *stdFp, const std::string &filename, int openFlags, int accessMode); ~VfsEncryption(); /*** * Plain version of the file related accessors ***/ /** * @return the size of the plain text file */ int64_t fileSizeGet() const noexcept; /* Read from file at given offset the requested size */ std::vector read(size_t offset, size_t count) const; /* write to file at given offset the requested size */ size_t write(const std::vector &plainData, size_t offset); /* Truncate the file to the given size, if given size is greater than current, pad with 0 */ void truncate(const uint64_t size); /** * Get the filename * @return a string with the filename as given to the open function */ std::string filenameGet() const noexcept; /*** * Encryption related API ***/ /** * Set an encryption suite. * When called at file creation, select the module to use for this file * When called at the opening of an existing file, check it is the suite used at file creation, throw an exception if they differs */ void encryptionSuiteSet(const EncryptionSuite); /** * Set the secret Material in the encryption module * This function cannot be called if a encryption suite was not set. */ void secretMaterialSet(const std::vector &secretMaterial); /** * Returns the encryption suite used for this file * Can be return unset if the file is being created */ EncryptionSuite encryptionSuiteGet() const noexcept; /** * Returns the size of chunks in which the file is divided for encryption */ size_t chunkSizeGet() const noexcept; /** * Set the size, in bytes, of chunks in which the file is divided for encryption * This size must be a multiple of 16, accepted values in range [16, (2^16-1)*16]. * If the size is set on an existing file and differs from previous setting, an exception is generated * Default chunk size at file creation is 4kB. * A file holds a maximum of 2^32-1 chunks. 16 bytes chunks - not recommended smallest admissible value - limit the file size to 64GB */ void chunkSizeSet(const size_t size); /** * Get raw header: encryption module might check integrity on header * This function returns the raw header, without the encryption module part */ const std::vector& rawHeaderGet() const noexcept; }; } // namespace bctoolbox #endif /* BCTBX_VFS_STANDARD_HH */ bctoolbox-5.2.0/include/bctoolbox/vfs_standard.h000066400000000000000000000020421434566643100217700ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VFS_STANDARD_H #define BCTBX_VFS_STANDARD_H #include "bctoolbox/vfs.h" #include "bctoolbox/port.h" #ifdef __cplusplus extern "C"{ #endif /** * Virtual File sytem provided */ extern BCTBX_PUBLIC bctbx_vfs_t bcStandardVfs; #ifdef __cplusplus } #endif #endif /* BCTBX_VFS_STANDARD_H */ bctoolbox-5.2.0/src/000077500000000000000000000000001434566643100143145ustar00rootroot00000000000000bctoolbox-5.2.0/src/CMakeLists.txt000066400000000000000000000305141434566643100170570ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2016 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(BCTOOLBOX_C_SOURCE_FILES containers/list.c logging/logging.c parser.c utils/port.c vconnect.c vfs/vfs.c vfs/vfs_standard.c param_string.c ) set(BCTOOLBOX_CXX_SOURCE_FILES containers/map.cc conversion/charconv_encoding.cc utils/exception.cc utils/regex.cc utils/utils.cc ) set(BCTOOLBOX_PRIVATE_HEADER_FILES vfs/vfs_encryption_module.hh vfs/vfs_encryption_module_dummy.hh vfs/vfs_encryption_module_aes256gcm_sha256.hh ) if(APPLE) set(BCTOOLBOX_OBJC_SOURCE_FILES utils/ios_utils.mm ) set(BCTOOLBOX_IOS_OBJC_SOURCE_FILES utils/ios_utils_app.mm ) endif() if(WIN32) list(APPEND BCTOOLBOX_CXX_SOURCE_FILES utils/win_utils.cc) endif() if(ANDROID) list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv_android.cc) elseif(WIN32) list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv_windows.cc) else() list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv.cc) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") list(APPEND STRICT_OPTIONS_CXX "-x c++") endif() if(WIN32) list(APPEND STRICT_OPTIONS_CXX "/EHa") add_definitions(-EHa) endif() if(MBEDTLS_FOUND OR POLARSSL_FOUND) list(APPEND BCTOOLBOX_C_SOURCE_FILES crypto/crypto.c) list(APPEND BCTOOLBOX_CXX_SOURCE_FILES crypto/ecc.cc) endif() if(MBEDTLS_FOUND) list(APPEND BCTOOLBOX_C_SOURCE_FILES crypto/mbedtls.c) list(APPEND BCTOOLBOX_CXX_SOURCE_FILES crypto/mbedtls.cc vfs/vfs_encrypted.cc vfs/vfs_encryption_module_dummy.cc vfs/vfs_encryption_module_aes256gcm_sha256.cc) endif() if(POLARSSL_FOUND) list(APPEND BCTOOLBOX_C_SOURCE_FILES crypto/polarssl.c) endif() if(ENABLE_TESTS_COMPONENT) set(BCTOOLBOX_C_TESTER_SOURCE_FILES tester.c) endif() set(BCTOOLBOX_SOURCE_FILES ${BCTOOLBOX_C_SOURCE_FILES} ${BCTOOLBOX_CXX_SOURCE_FILES} ${BCTOOLBOX_OBJC_SOURCE_FILES}) set(BCTOOLBOX_IOS_SOURCE_FILES ${BCTOOLBOX_IOS_OBJC_SOURCE_FILES}) set(BCTOOLBOX_TESTER_SOURCE_FILES ${BCTOOLBOX_C_TESTER_SOURCE_FILES}) bc_apply_compile_flags(BCTOOLBOX_C_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) bc_apply_compile_flags(BCTOOLBOX_C_TESTER_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C) bc_apply_compile_flags(BCTOOLBOX_CXX_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX) bc_apply_compile_flags(BCTOOLBOX_OBJC_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC) bc_apply_compile_flags(BCTOOLBOX_IOS_OBJC_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC) if(ENABLE_STATIC) add_library(bctoolbox-static STATIC ${BCTOOLBOX_SOURCE_FILES} ${BCTOOLBOX_HEADER_FILES} ${BCTOOLBOX_PRIVATE_HEADER_FILES}) target_link_libraries(bctoolbox-static INTERFACE ${CMAKE_THREAD_LIBS_INIT}) if(APPLE) target_link_libraries(bctoolbox-static INTERFACE "-framework Foundation" "-framework AVFoundation") target_include_directories(bctoolbox-static SYSTEM PRIVATE ${ICONV_INCLUDE_DIRS}) target_link_libraries(bctoolbox-static INTERFACE ${ICONV_LIBRARIES}) endif() if(WIN32) target_link_libraries(bctoolbox-static INTERFACE "Winmm" "Ws2_32") endif() set_target_properties(bctoolbox-static PROPERTIES LINKER_LANGUAGE "CXX") set_target_properties(bctoolbox-static PROPERTIES OUTPUT_NAME bctoolbox) if(HAVE_LIBRT) target_link_libraries(bctoolbox-static INTERFACE rt) endif() if(HAVE_LIBDL) target_link_libraries(bctoolbox-static INTERFACE dl) endif() if(ANDROID) target_link_libraries(bctoolbox-static INTERFACE log) endif() if(ENABLE_TESTS_COMPONENT) add_library(bctoolbox-tester-static STATIC ${BCTOOLBOX_TESTER_SOURCE_FILES}) set_target_properties(bctoolbox-tester-static PROPERTIES OUTPUT_NAME bctoolbox-tester) target_link_libraries(bctoolbox-tester-static INTERFACE bctoolbox-static) endif() endif() if(ENABLE_SHARED) add_library(bctoolbox SHARED ${BCTOOLBOX_SOURCE_FILES} ${BCTOOLBOX_HEADER_FILES} ${BCTOOLBOX_PRIVATE_HEADER_FILES}) target_link_libraries(bctoolbox PRIVATE ${CMAKE_THREAD_LIBS_INIT}) if(APPLE) if(IOS) set(MIN_OS ${LINPHONE_IOS_DEPLOYMENT_TARGET}) else() set(MIN_OS ${CMAKE_OSX_DEPLOYMENT_TARGET}) endif() set_target_properties(bctoolbox PROPERTIES FRAMEWORK TRUE MACOSX_FRAMEWORK_IDENTIFIER org.linphone.bctoolbox MACOSX_FRAMEWORK_INFO_PLIST "${PROJECT_SOURCE_DIR}/build/osx/Info.plist.in" PUBLIC_HEADER "${BCTOOLBOX_HEADER_FILES}" ) endif() if(APPLE) if (IOS) target_link_libraries(bctoolbox PRIVATE "-framework UIKit" "-framework Foundation" "-framework AVFoundation") else() target_link_libraries(bctoolbox PRIVATE "-framework Foundation" "-framework AVFoundation") endif() target_include_directories(bctoolbox SYSTEM PRIVATE ${ICONV_INCLUDE_DIRS}) target_link_libraries(bctoolbox PRIVATE ${ICONV_LIBRARIES}) endif() if(WIN32) set_target_properties(bctoolbox PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") target_link_libraries(bctoolbox PRIVATE "Winmm" "Ws2_32") endif() target_compile_definitions(bctoolbox PRIVATE "-DBCTBX_EXPORTS") if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")#Fix error on 'vccorlib_lib_should_be_specified_before_msvcrt_lib_to_linker'' get_target_property(BC_LINK_FLAGS bctoolbox LINK_FLAGS) if(NOT BC_LINK_FLAGS) set(BC_LINK_FLAGS "") endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND BC_LINK_FLAGS "/nodefaultlib:vccorlibd /nodefaultlib:msvcrtd vccorlibd.lib msvcrtd.lib") else() list(APPEND BC_LINK_FLAGS "/nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib") endif() set_target_properties(bctoolbox PROPERTIES VS_WINRT_EXTENSIONS TRUE) add_definitions(-ZW -EHsc) set_target_properties(bctoolbox PROPERTIES LINK_FLAGS "${BC_LINK_FLAGS}") endif() endif() set_target_properties(bctoolbox PROPERTIES LINKER_LANGUAGE "CXX") if(NOT ANDROID) # Do not version shared library on Android set_target_properties(bctoolbox PROPERTIES SOVERSION ${BCTOOLBOX_SO_VERSION}) endif() if(MSVC) if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() endif() if(HAVE_LIBRT) target_link_libraries(bctoolbox PRIVATE rt) endif() if(HAVE_LIBDL) target_link_libraries(bctoolbox PRIVATE dl) endif() if(ANDROID) target_link_libraries(bctoolbox PRIVATE log) endif() if(ENABLE_TESTS_COMPONENT) add_library(bctoolbox-tester SHARED ${BCTOOLBOX_TESTER_SOURCE_FILES}) set_target_properties(bctoolbox-tester PROPERTIES LINKER_LANGUAGE "CXX") target_link_libraries(bctoolbox-tester PUBLIC bctoolbox PRIVATE ${BCUNIT_TARGETNAME}) if(WIN32) target_compile_definitions(bctoolbox-tester PRIVATE "-DBCTBX_EXPORTS") endif() if(NOT ANDROID) # Do not version shared library on Android set_target_properties(bctoolbox-tester PROPERTIES SOVERSION ${BCTOOLBOXTESTER_SO_VERSION}) endif() if(MSVC) if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) endif() endif() endif() endif() if(ENABLE_STATIC) target_include_directories(bctoolbox-static PUBLIC $ $ ) endif() if(ENABLE_SHARED) target_include_directories(bctoolbox PUBLIC $ $ ) endif() if(APPLE) if(IOS) add_library(bctoolbox-ios SHARED ${BCTOOLBOX_IOS_SOURCE_FILES}) target_link_libraries(bctoolbox-ios PRIVATE bctoolbox) set_target_properties(bctoolbox-ios PROPERTIES FRAMEWORK TRUE MACOSX_FRAMEWORK_IDENTIFIER org.linphone.bctoolbox-ios MACOSX_FRAMEWORK_INFO_PLIST "${PROJECT_SOURCE_DIR}/build/osx/Info.plist.in" LINK_FLAGS "-framework UIKit -framework Foundation -framework CoreFoundation" ) endif() endif() if(MBEDTLS_FOUND) if(ENABLE_STATIC) target_link_libraries(bctoolbox-static INTERFACE ${MBEDTLS_TARGETS}) endif() if(ENABLE_SHARED) target_link_libraries(bctoolbox PRIVATE ${MBEDTLS_TARGETS}) endif() endif() if(POLARSSL_FOUND) if(ENABLE_STATIC) target_include_directories(bctoolbox-static SYSTEM PRIVATE ${POLARSSL_INCLUDE_DIRS}) target_link_libraries(bctoolbox-static INTERFACE ${POLARSSL_LIBRARIES}) endif() if(ENABLE_SHARED) target_include_directories(bctoolbox PRIVATE ${POLARSSL_INCLUDE_DIRS}) target_link_libraries(bctoolbox PRIVATE ${POLARSSL_LIBRARIES}) endif() endif() if(DECAF_FOUND) if(ENABLE_STATIC) target_include_directories(bctoolbox-static PRIVATE ${DECAF_INCLUDE_DIRS}) target_link_libraries(bctoolbox-static PUBLIC decaf-static) endif() if(ENABLE_SHARED) target_include_directories(bctoolbox PRIVATE ${DECAF_INCLUDE_DIRS}) target_link_libraries(bctoolbox PRIVATE ${DECAF_TARGETNAME}) endif() endif() if(ENABLE_TESTS_COMPONENT) if(ENABLE_STATIC) target_link_libraries(bctoolbox-tester-static PUBLIC bctoolbox-static ${BCUNIT_TARGETNAME}) endif() if(ENABLE_SHARED) if(APPLE) set_target_properties(bctoolbox-tester PROPERTIES FRAMEWORK TRUE MACOSX_FRAMEWORK_IDENTIFIER com.belledonne-communications.bctoolbox-tester MACOSX_FRAMEWORK_INFO_PLIST "${PROJECT_SOURCE_DIR}/build/osx/Info.plist.in" PUBLIC_HEADER "${BCTOOLBOX_HEADER_FILES}" ) endif() endif() endif() if(ENABLE_STATIC) install(TARGETS bctoolbox-static 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 COMPONENT core ) if(ENABLE_TESTS_COMPONENT) install(TARGETS bctoolbox-tester-static 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 COMPONENT tester ) endif() endif() if(ENABLE_SHARED) install(TARGETS bctoolbox EXPORT ${EXPORT_TARGETS_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION Frameworks PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT core ) if(ENABLE_TESTS_COMPONENT) install(TARGETS bctoolbox-tester EXPORT ${EXPORT_TARGETS_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION Frameworks PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT tester ) endif() endif() if(APPLE) if(IOS) install(TARGETS bctoolbox-ios EXPORT ${EXPORT_TARGETS_NAME}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION Frameworks PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT core ) endif() endif() if(MSVC) # Disable "was deprecated" warnings of windows compiler (typically using strcpy_s instead of strcpy and stupid things like this). if(ENABLE_SHARED) target_compile_options(bctoolbox PRIVATE "/wd4996") endif() if(ENABLE_STATIC) target_compile_options(bctoolbox-static PRIVATE "/wd4996") endif() if(ENABLE_TESTS_COMPONENT) target_compile_options(bctoolbox-tester PRIVATE "/wd4996") endif() endif() bctoolbox-5.2.0/src/containers/000077500000000000000000000000001434566643100164615ustar00rootroot00000000000000bctoolbox-5.2.0/src/containers/list.c000066400000000000000000000206731434566643100176100ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #define _CRT_RAND_S #include #include #include #ifndef _WIN32 #include #include /*for gettimeofday*/ #include /* available on POSIX system only */ #else #include #endif #include "bctoolbox/port.h" #include "bctoolbox/logging.h" #include "bctoolbox/list.h" bctbx_list_t* bctbx_list_new(void *data){ bctbx_list_t* new_elem=bctbx_new0(bctbx_list_t,1); new_elem->data=data; return new_elem; } bctbx_list_t* bctbx_list_next(const bctbx_list_t *elem) { return elem->next; } void* bctbx_list_get_data(const bctbx_list_t *elem) { return elem->data; } bctbx_list_t* bctbx_list_append_link(bctbx_list_t* elem,bctbx_list_t *new_elem){ bctbx_list_t* it=elem; if (elem==NULL) return new_elem; if (new_elem==NULL) return elem; while (it->next!=NULL) it=bctbx_list_next(it); it->next=new_elem; new_elem->prev=it; return elem; } bctbx_list_t* bctbx_list_append(bctbx_list_t* elem, void * data){ bctbx_list_t* new_elem=bctbx_list_new(data); return bctbx_list_append_link(elem,new_elem); } bctbx_list_t* bctbx_list_prepend_link(bctbx_list_t* elem, bctbx_list_t *new_elem){ if (elem!=NULL) { new_elem->next=elem; elem->prev=new_elem; } return new_elem; } bctbx_list_t* bctbx_list_prepend(bctbx_list_t* elem, void *data){ return bctbx_list_prepend_link(elem,bctbx_list_new(data)); } bctbx_list_t * bctbx_list_last_elem(const bctbx_list_t *l){ if (l==NULL) return NULL; while(l->next){ l=l->next; } return (bctbx_list_t*)l; } bctbx_list_t * bctbx_list_first_elem(const bctbx_list_t *l){ if (l==NULL) return NULL; while(l->prev){ l=l->prev; } return (bctbx_list_t*)l; } bctbx_list_t* bctbx_list_concat(bctbx_list_t* first, bctbx_list_t* second){ bctbx_list_t* it=first; if (it==NULL) return second; if (second==NULL) return first; while(it->next!=NULL) it=bctbx_list_next(it); it->next=second; second->prev=it; return first; } bctbx_list_t* bctbx_list_free(bctbx_list_t* list){ bctbx_list_t* elem = list; bctbx_list_t* tmp; if (list==NULL) return NULL; while(elem->next!=NULL) { tmp = elem; elem = elem->next; bctbx_free(tmp); } bctbx_free(elem); return NULL; } bctbx_list_t * bctbx_list_free_with_data(bctbx_list_t *list, bctbx_list_free_func freefunc){ bctbx_list_t* elem = list; bctbx_list_t* tmp; if (list==NULL) return NULL; while(elem->next!=NULL) { tmp = elem; elem = elem->next; freefunc(tmp->data); bctbx_free(tmp); } freefunc(elem->data); bctbx_free(elem); return NULL; } bctbx_list_t* _bctbx_list_remove(bctbx_list_t* first, void *data, int warn_if_not_found){ bctbx_list_t* it; it=bctbx_list_find(first,data); if (it) return bctbx_list_erase_link(first,it); else if (warn_if_not_found){ bctbx_warning("bctbx_list_remove: no element with %p data was in the list", data); } return first; } bctbx_list_t* bctbx_list_remove(bctbx_list_t* first, void *data){ return _bctbx_list_remove(first, data, TRUE); } bctbx_list_t * bctbx_list_remove_custom(bctbx_list_t *first, bctbx_compare_func compare_func, const void *user_data) { bctbx_list_t *cur; bctbx_list_t *elem = first; while (elem != NULL) { cur = elem; elem = elem->next; if (compare_func(cur->data, user_data) == 0) { first = bctbx_list_remove(first, cur->data); } } return first; } size_t bctbx_list_size(const bctbx_list_t* first){ size_t n=0; while(first!=NULL){ ++n; first=first->next; } return n; } void bctbx_list_for_each(const bctbx_list_t* list, bctbx_list_iterate_func func){ for(;list!=NULL;list=list->next){ func(list->data); } } void bctbx_list_for_each2(const bctbx_list_t* list, bctbx_list_iterate2_func func, void *user_data){ for(;list!=NULL;list=list->next){ func(list->data,user_data); } } bctbx_list_t * bctbx_list_pop_front(bctbx_list_t *list, void **front_data){ bctbx_list_t *front_elem=list; if (front_elem==NULL){ *front_data=NULL; return NULL; } *front_data=front_elem->data; list=bctbx_list_unlink(list,front_elem); bctbx_free(front_elem); return list; } bctbx_list_t* bctbx_list_unlink(bctbx_list_t* list, bctbx_list_t* elem){ bctbx_list_t* ret; if (elem==list){ ret=elem->next; elem->prev=NULL; elem->next=NULL; if (ret!=NULL) ret->prev=NULL; return ret; } elem->prev->next=elem->next; if (elem->next!=NULL) elem->next->prev=elem->prev; elem->next=NULL; elem->prev=NULL; return list; } bctbx_list_t * bctbx_list_erase_link(bctbx_list_t* list, bctbx_list_t* elem){ bctbx_list_t *ret=bctbx_list_unlink(list,elem); bctbx_free(elem); return ret; } bctbx_list_t* bctbx_list_find(bctbx_list_t* list, const void *data){ for(;list!=NULL;list=list->next){ if (list->data==data) return list; } return NULL; } bctbx_list_t* bctbx_list_find_custom(const bctbx_list_t* list, bctbx_compare_func compare_func, const void *user_data){ for(;list!=NULL;list=list->next){ if (compare_func(list->data,user_data)==0) return (bctbx_list_t *)list; } return NULL; } bctbx_list_t *bctbx_list_delete_custom(bctbx_list_t *list, bctbx_compare_func compare_func, const void *user_data){ bctbx_list_t *elem=bctbx_list_find_custom(list,compare_func,user_data); if (elem!=NULL){ list=bctbx_list_erase_link(list,elem); } return list; } void * bctbx_list_nth_data(const bctbx_list_t* list, int index){ int i; for(i=0;list!=NULL;list=list->next,++i){ if (i==index) return list->data; } bctbx_error("bctbx_list_nth_data: no such index in list."); return NULL; } int bctbx_list_position(const bctbx_list_t* list, bctbx_list_t* elem){ int i; for(i=0;list!=NULL;list=list->next,++i){ if (elem==list) return i; } bctbx_error("bctbx_list_position: no such element in list."); return -1; } int bctbx_list_index(const bctbx_list_t* list, void *data){ int i; for(i=0;list!=NULL;list=list->next,++i){ if (data==list->data) return i; } bctbx_error("bctbx_list_index: no such element in list."); return -1; } bctbx_list_t* bctbx_list_insert_sorted(bctbx_list_t* list, void *data, bctbx_compare_func compare_func){ bctbx_list_t* it,*previt=NULL; bctbx_list_t* nelem; bctbx_list_t* ret=list; if (list==NULL) return bctbx_list_append(list,data); else{ nelem=bctbx_list_new(data); for(it=list;it!=NULL;it=it->next){ previt=it; if (compare_func(data,it->data)<=0){ nelem->prev=it->prev; nelem->next=it; if (it->prev!=NULL) it->prev->next=nelem; else{ ret=nelem; } it->prev=nelem; return ret; } } previt->next=nelem; nelem->prev=previt; } return ret; } bctbx_list_t* bctbx_list_insert(bctbx_list_t* list, bctbx_list_t* before, void *data){ bctbx_list_t* elem; if (list==NULL || before==NULL) return bctbx_list_append(list,data); for(elem=list;elem!=NULL;elem=bctbx_list_next(elem)){ if (elem==before){ if (elem->prev==NULL) return bctbx_list_prepend(list,data); else{ bctbx_list_t* nelem=bctbx_list_new(data); nelem->prev=elem->prev; nelem->next=elem; elem->prev->next=nelem; elem->prev=nelem; } } } return list; } bctbx_list_t* bctbx_list_copy(const bctbx_list_t* list){ bctbx_list_t* copy=NULL; const bctbx_list_t* iter; for(iter=list;iter!=NULL;iter=bctbx_list_next(iter)){ copy=bctbx_list_append(copy,iter->data); } return copy; } bctbx_list_t* bctbx_list_copy_with_data(const bctbx_list_t* list, bctbx_list_copy_func copyfunc){ bctbx_list_t* copy=NULL; const bctbx_list_t* iter; for(iter=list;iter!=NULL;iter=bctbx_list_next(iter)){ copy=bctbx_list_append(copy,copyfunc(iter->data)); } return copy; } bctbx_list_t* bctbx_list_copy_reverse_with_data(const bctbx_list_t* list, bctbx_list_copy_func copyfunc){ bctbx_list_t* copy=NULL; const bctbx_list_t* iter; for(iter=list;iter!=NULL;iter=bctbx_list_next(iter)){ copy=bctbx_list_prepend(copy,copyfunc(iter->data)); } return copy; } bctoolbox-5.2.0/src/containers/map.cc000066400000000000000000000272631434566643100175570ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/logging.h" #include "bctoolbox/map.h" #include #include #define LOG_DOMAIN "bctoolbox" typedef std::multimap mmap_ullong_t; typedef mmap_ullong_t::value_type pair_ullong_t; typedef std::multimap mmap_cchar_t; typedef mmap_cchar_t::value_type pair_cchar_t; template bctbx_map_t * bctbx_mmap_new(void) { return (bctbx_map_t *) new T; } extern "C" bctbx_map_t *bctbx_mmap_ullong_new(void) { return bctbx_mmap_new(); } extern "C" bctbx_map_t *bctbx_mmap_cchar_new(void) { return bctbx_mmap_new(); } template void bctbx_mmap_delete(bctbx_map_t *mmap) { delete (T *)mmap; } extern "C" void bctbx_mmap_ullong_delete(bctbx_map_t *mmap) { bctbx_mmap_delete(mmap); } extern "C" void bctbx_mmap_cchar_delete(bctbx_map_t *mmap) { bctbx_mmap_delete(mmap); } extern "C" void bctbx_mmap_ullong_delete_with_data(bctbx_map_t *mmap, bctbx_map_free_func freefunc) { bctbx_iterator_t *it = bctbx_map_ullong_begin(mmap); bctbx_iterator_t *end = bctbx_map_ullong_end(mmap); while(!bctbx_iterator_ullong_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_ullong_get_pair(it); freefunc(bctbx_pair_ullong_get_second(pair)); it = bctbx_iterator_ullong_get_next(it); } bctbx_iterator_ullong_delete(it); bctbx_iterator_ullong_delete(end); bctbx_mmap_ullong_delete(mmap); } extern "C" void bctbx_mmap_cchar_delete_with_data(bctbx_map_t *mmap, bctbx_map_free_func freefunc) { bctbx_iterator_t *it = bctbx_map_cchar_begin(mmap); bctbx_iterator_t *end = bctbx_map_cchar_end(mmap); while(!bctbx_iterator_cchar_equals(it, end)) { bctbx_pair_t *pair = bctbx_iterator_cchar_get_pair(it); freefunc(bctbx_pair_cchar_get_second(pair)); it = bctbx_iterator_cchar_get_next(it); } bctbx_iterator_cchar_delete(it); bctbx_iterator_cchar_delete(end); bctbx_mmap_cchar_delete(mmap); } template bctbx_iterator_t *bctbx_map_insert_base(bctbx_map_t *map,const bctbx_pair_t *pair,bool_t returns_it) { typename T::iterator it; it = ((T *)map)->insert(*((typename T::value_type *)pair)); if (returns_it) { return (bctbx_iterator_t *) new typename T::iterator(it); } else return NULL; } static bctbx_iterator_t * bctbx_map_ullong_insert_base(bctbx_map_t *map,const bctbx_pair_t *pair,bool_t returns_it) { return bctbx_map_insert_base(map, pair, returns_it); } extern "C" void bctbx_map_ullong_insert(bctbx_map_t *map,const bctbx_pair_t *pair) { bctbx_map_ullong_insert_base(map,pair,FALSE); } static bctbx_iterator_t * bctbx_map_cchar_insert_base(bctbx_map_t *map,const bctbx_pair_t *pair,bool_t returns_it) { return bctbx_map_insert_base(map, pair, returns_it); } extern "C" void bctbx_map_cchar_insert(bctbx_map_t *map,const bctbx_pair_t *pair) { bctbx_map_cchar_insert_base(map,pair,FALSE); } extern "C" void bctbx_map_ullong_insert_and_delete(bctbx_map_t *map, bctbx_pair_t *pair) { bctbx_map_ullong_insert(map,pair); bctbx_pair_ullong_delete(pair); } extern "C" bctbx_iterator_t * bctbx_map_ullong_insert_and_delete_with_returned_it(bctbx_map_t *map, bctbx_pair_t *pair) { bctbx_iterator_t * it = bctbx_map_ullong_insert_base(map,pair,TRUE); bctbx_pair_ullong_delete(pair); return it; } extern "C" void bctbx_map_cchar_insert_and_delete(bctbx_map_t *map, bctbx_pair_t *pair) { bctbx_map_cchar_insert(map,pair); bctbx_pair_cchar_delete(pair); } extern "C" bctbx_iterator_t * bctbx_map_cchar_insert_and_delete_with_returned_it(bctbx_map_t *map, bctbx_pair_t *pair) { bctbx_iterator_t * it = bctbx_map_cchar_insert_base(map,pair,TRUE); bctbx_pair_cchar_delete(pair); return it; } template bctbx_iterator_t *bctbx_map_erase_type(bctbx_map_t *map,bctbx_iterator_t *it) { //bctbx_iterator_t * next = (bctbx_iterator_t *) new mmap_ullong_t::iterator((*(mmap_ullong_t::iterator*)it)); //next = bctbx_iterator_get_next(next); ((T *)map)->erase((*(typename T::iterator*)it)++); //bctbx_iterator_delete(it); return it; } extern "C" bctbx_iterator_t *bctbx_map_ullong_erase(bctbx_map_t *map,bctbx_iterator_t *it) { return bctbx_map_erase_type(map, it); } extern "C" bctbx_iterator_t *bctbx_map_cchar_erase(bctbx_map_t *map,bctbx_iterator_t *it) { return bctbx_map_erase_type(map, it); } template bctbx_iterator_t *bctbx_map_begin_type(const bctbx_map_t *map) { return (bctbx_iterator_t *) new typename T::iterator(((T *)map)->begin()); } extern "C" bctbx_iterator_t *bctbx_map_ullong_begin(const bctbx_map_t *map) { return bctbx_map_begin_type(map); } extern "C" bctbx_iterator_t *bctbx_map_cchar_begin(const bctbx_map_t *map) { return bctbx_map_begin_type(map); } template bctbx_iterator_t *bctbx_map_end_type(const bctbx_map_t *map) { return (bctbx_iterator_t *) new typename T::iterator(((T *)map)->end()); } extern "C" bctbx_iterator_t *bctbx_map_ullong_end(const bctbx_map_t *map) { return bctbx_map_end_type(map); } extern "C" bctbx_iterator_t *bctbx_map_cchar_end(const bctbx_map_t *map) { return bctbx_map_end_type(map); } template bctbx_iterator_t * bctbx_map_find_key_type(const bctbx_map_t *map, typename T::key_type key) { bctbx_iterator_t * it = (bctbx_iterator_t*) new typename T::iterator(((T *)map)->find(key)); return it; } extern "C" bctbx_iterator_t * bctbx_map_ullong_find_key(const bctbx_map_t *map, unsigned long long key) { return bctbx_map_find_key_type(map, (mmap_ullong_t::key_type)key); } extern "C" bctbx_iterator_t * bctbx_map_cchar_find_key(const bctbx_map_t *map, const char * key) { return bctbx_map_find_key_type(map, (mmap_cchar_t::key_type)key); } template size_t bctbx_map_size_type(const bctbx_map_t *map) { return ((T *)map)->size(); } extern "C" size_t bctbx_map_ullong_size(const bctbx_map_t *map) { return bctbx_map_size_type(map); } extern "C" size_t bctbx_map_cchar_size(const bctbx_map_t *map) { return bctbx_map_size_type(map); } extern "C" bctbx_iterator_t * bctbx_map_ullong_find_custom(const bctbx_map_t *map, bctbx_compare_func compare_func, const void *user_data) { bctbx_iterator_t * end = bctbx_map_ullong_end(map); for(bctbx_iterator_t * it = bctbx_map_ullong_begin(map);!bctbx_iterator_ullong_equals(it,end);) { if (compare_func(bctbx_pair_ullong_get_second(bctbx_iterator_ullong_get_pair(it)),user_data)==0) { bctbx_iterator_ullong_delete(end); return it; } else { it = bctbx_iterator_ullong_get_next(it); } } bctbx_iterator_ullong_delete(end); return NULL; } extern "C" bctbx_iterator_t * bctbx_map_cchar_find_custom(const bctbx_map_t *map, bctbx_compare_func compare_func, const void *user_data) { bctbx_iterator_t * end = bctbx_map_cchar_end(map); for(bctbx_iterator_t * it = bctbx_map_cchar_begin(map);!bctbx_iterator_cchar_equals(it,end);) { if (compare_func(bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it)),user_data)==0) { bctbx_iterator_cchar_delete(end); return it; } else { it = bctbx_iterator_cchar_get_next(it); } } bctbx_iterator_cchar_delete(end); return NULL; } /*iterator*/ template bctbx_pair_t *bctbx_iterator_get_pair_type(const bctbx_iterator_t *it) { return (bctbx_pair_t *)&(**((typename T::iterator*)it)); } extern "C" bctbx_pair_t *bctbx_iterator_ullong_get_pair(const bctbx_iterator_t *it) { return bctbx_iterator_get_pair_type(it); } extern "C" bctbx_pair_t *bctbx_iterator_cchar_get_pair(const bctbx_iterator_t *it) { return bctbx_iterator_get_pair_type(it); } template bctbx_iterator_t *bctbx_iterator_get_next_type(bctbx_iterator_t *it) { ((typename T::iterator*)it)->operator++(); return it; } extern "C" bctbx_iterator_t *bctbx_iterator_ullong_get_next(bctbx_iterator_t *it) { return bctbx_iterator_get_next_type(it); } extern "C" bctbx_iterator_t *bctbx_iterator_cchar_get_next(bctbx_iterator_t *it) { return bctbx_iterator_get_next_type(it); } extern "C" bctbx_iterator_t *bctbx_iterator_ullong_get_next_and_delete(bctbx_iterator_t *it) { bctbx_iterator_t * next = bctbx_iterator_ullong_get_next(it); bctbx_iterator_ullong_delete(it); return next; } extern "C" bctbx_iterator_t *bctbx_iterator_cchar_get_next_and_delete(bctbx_iterator_t *it) { bctbx_iterator_t * next = bctbx_iterator_cchar_get_next(it); bctbx_iterator_cchar_delete(it); return next; } template bool_t bctbx_iterator_equals_type(const bctbx_iterator_t *a,const bctbx_iterator_t *b) { return *(typename T::iterator*)a == *(typename T::iterator*)b; } extern "C" bool_t bctbx_iterator_ullong_equals(const bctbx_iterator_t *a,const bctbx_iterator_t *b) { return bctbx_iterator_equals_type(a, b); } extern "C" bool_t bctbx_iterator_cchar_equals(const bctbx_iterator_t *a,const bctbx_iterator_t *b) { return bctbx_iterator_equals_type(a, b); } template void bctbx_iterator_delete_type(bctbx_iterator_t *it) { delete ((typename T::iterator*)it); } extern "C" void bctbx_iterator_ullong_delete(bctbx_iterator_t *it) { bctbx_iterator_delete_type(it); } extern "C" void bctbx_iterator_cchar_delete(bctbx_iterator_t *it) { bctbx_iterator_delete_type(it); } /*pair*/ template typename T::value_type * bctbx_pair_new(typename T::key_type key,void *value) { return (typename T::value_type *) new typename T::value_type(key,value); } extern "C" bctbx_pair_ullong_t * bctbx_pair_ullong_new(unsigned long long key,void *value) { return (bctbx_pair_ullong_t *)bctbx_pair_new((mmap_ullong_t::key_type)key, value); } extern "C" bctbx_pair_cchar_t * bctbx_pair_cchar_new(const char * key,void *value) { return (bctbx_pair_cchar_t *)bctbx_pair_new((mmap_cchar_t::key_type)key, value); } template const typename T::key_type& bctbx_pair_get_first(const typename T::value_type * pair) { return ((typename T::value_type*)pair)->first; } extern "C" unsigned long long bctbx_pair_ullong_get_first(const bctbx_pair_ullong_t * pair) { return bctbx_pair_get_first((pair_ullong_t *)pair); } extern "C" const char * bctbx_pair_cchar_get_first(const bctbx_pair_cchar_t * pair) { return bctbx_pair_get_first((pair_cchar_t *)pair).c_str(); } template void* bctbx_pair_get_second_type(const bctbx_pair_t * pair) { return ((T*)pair)->second; } extern "C" void* bctbx_pair_ullong_get_second(const bctbx_pair_t * pair) { return bctbx_pair_get_second_type(pair); } extern "C" void* bctbx_pair_cchar_get_second(const bctbx_pair_t * pair) { return bctbx_pair_get_second_type(pair); } template void bctbx_pair_delete_type(bctbx_pair_t * pair) { delete ((T*)pair); } extern "C" void bctbx_pair_ullong_delete(bctbx_pair_t * pair) { bctbx_pair_delete_type(pair); } extern "C" void bctbx_pair_cchar_delete(bctbx_pair_t * pair) { bctbx_pair_delete_type(pair); } bctoolbox-5.2.0/src/conversion/000077500000000000000000000000001434566643100165015ustar00rootroot00000000000000bctoolbox-5.2.0/src/conversion/charconv.cc000066400000000000000000000065121434566643100206170ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "bctoolbox/charconv.h" #include "bctoolbox/logging.h" #include "bctoolbox/port.h" static char *convert_from_to (const char *str, const char *from, const char *to) { if (!from || !to) return NULL; if (strcasecmp(from, to) == 0) return bctbx_strdup(str); char *in_buf = (char *) str; char *out_buf, *ptr; size_t in_left = strlen(str) + 1; size_t out_left = in_left + in_left/10; // leave a marge of 10% iconv_t cd; setlocale(LC_CTYPE, ""); const char* r_from = strcasecmp("locale", from) == 0 ? nl_langinfo(CODESET) : from; const char* r_to = strcasecmp("locale", to) == 0 ? nl_langinfo(CODESET) : to; if (strcasecmp(r_from, r_to) == 0) { return bctbx_strdup(str); } cd = iconv_open(r_to, r_from); if (cd != (iconv_t)-1) { size_t ret; size_t out_len = out_left; out_buf = (char *) bctbx_malloc(out_left); ptr = out_buf; // Keep a pointer to the beginning of this buffer to be able to realloc ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left); while (ret == (size_t)-1 && errno == E2BIG) { ptr = (char *) bctbx_realloc(ptr, out_len*2); out_left = out_len; out_buf = ptr + out_left; out_len *= 2; ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left); } iconv_close(cd); if (ret == (size_t)-1 && errno != E2BIG) { bctbx_error("Error while converting a string from '%s' to '%s': %s", from, to, strerror(errno)); bctbx_free(ptr); return bctbx_strdup(str); } } else { bctbx_error("Unable to open iconv content descriptor from '%s' to '%s': %s", from, to, strerror(errno)); return bctbx_strdup(str); } return ptr; } char *bctbx_locale_to_utf8 (const char *str) { const char *default_encoding = bctbx_get_default_encoding(); if (!strcmp(default_encoding, "UTF-8")) return bctbx_strdup(str); return convert_from_to(str, default_encoding, "UTF-8"); } char *bctbx_utf8_to_locale (const char *str) { const char *default_encoding = bctbx_get_default_encoding(); if (!strcmp(default_encoding, "UTF-8")) return bctbx_strdup(str); return convert_from_to(str, "UTF-8", default_encoding); } char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) { return convert_from_to(str, encoding, "UTF-8"); } wchar_t* bctbx_string_to_wide_string(const char* str){ // TODO bctbx_error("Conversion from string to wide string is not implemented"); return NULL; } unsigned int bctbx_get_code_page(const char* encoding){ bctbx_error("Getting code page is not implemented"); return 0; } bctoolbox-5.2.0/src/conversion/charconv_android.cc000066400000000000000000000032011434566643100223070ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/charconv.h" #include "bctoolbox/logging.h" #include "bctoolbox/port.h" char *bctbx_locale_to_utf8 (const char *str) { // TODO remove this part when the NDK will contain a usable iconv return bctbx_strdup(str); } char *bctbx_utf8_to_locale (const char *str) { // TODO remove this part when the NDK will contain a usable iconv return bctbx_strdup(str); } char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) { if (!encoding) return NULL; // TODO change this part when the NDK will contain a usable iconv return bctbx_strdup(str); } wchar_t* bctbx_string_to_wide_string(const char* str){ // TODO bctbx_error("Conversion from string to wide string is not implemented"); return NULL; } unsigned int bctbx_get_code_page(const char* encoding){ bctbx_error("Getting code page is not implemented"); return 0; }bctoolbox-5.2.0/src/conversion/charconv_encoding.cc000066400000000000000000000025071434566643100224650ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef __APPLE__ #include "TargetConditionals.h" #endif #include "bctoolbox/charconv.h" #include "bctoolbox/logging.h" #include "bctoolbox/port.h" namespace { std::string defaultEncoding = ""; } void bctbx_set_default_encoding (const char *encoding) { defaultEncoding = encoding; } const char *bctbx_get_default_encoding (void) { if (!defaultEncoding.empty()) return defaultEncoding.c_str(); #if defined(__ANDROID__) || TARGET_OS_IPHONE return "UTF-8"; #else return "locale"; #endif } bctoolbox-5.2.0/src/conversion/charconv_windows.cc000066400000000000000000000126751434566643100224000ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "bctoolbox/charconv.h" #include "bctoolbox/logging.h" #include "bctoolbox/port.h" static std::unordered_map windowsCharset { { "LOCALE", CP_ACP }, { "IBM037", 037 }, { "IBM437", 437 }, { "IBM500", 500 }, { "ASMO-708", 708 }, { "IBM775", 775 }, { "IBM850", 850 }, { "IBM852", 852 }, { "IBM855", 855 }, { "IBM857", 857 }, { "IBM860", 860 }, { "IBM861", 861 }, { "IBM863", 863 }, { "IBM864", 864 }, { "IBM865", 865 }, { "CP866", 866 }, { "IBM869", 869 }, { "IBM870", 870 }, { "WINDOWS-874", 874 }, { "CP875", 875 }, { "SHIFT_JIS", 932 }, { "GB2312", 936 }, { "BIG5", 950 }, { "IBM1026", 1026 }, { "UTF-16", 1200 }, { "WINDOWS-1250", 1250 }, { "WINDOWS-1251", 1251 }, { "WINDOWS-1252", 1252 }, { "WINDOWS-1253", 1253 }, { "WINDOWS-1254", 1254 }, { "WINDOWS-1255", 1255 }, { "WINDOWS-1256", 1256 }, { "WINDOWS-1257", 1257 }, { "WINDOWS-1258", 1258 }, { "JOHAB", 1361 }, { "MACINTOSH", 10000 }, { "UTF-32", 12000 }, { "UTF-32BE", 12001 }, { "US-ASCII", 20127 }, { "IBM273", 20273 }, { "IBM277", 20277 }, { "IBM278", 20278 }, { "IBM280", 20280 }, { "IBM284", 20284 }, { "IBM285", 20285 }, { "IBM290", 20290 }, { "IBM297", 20297 }, { "IBM420", 20420 }, { "IBM423", 20423 }, { "IBM424", 20424 }, { "KOI8-R", 20866 }, { "IBM871", 20871 }, { "IBM880", 20880 }, { "IBM905", 20905 }, { "EUC-JP", 20932 }, { "CP1025", 21025 }, { "KOI8-U", 21866 }, { "ISO-8859-1", 28591 }, { "ISO-8859-2", 28592 }, { "ISO-8859-3", 28593 }, { "ISO-8859-4", 28594 }, { "ISO-8859-5", 28595 }, { "ISO-8859-6", 28596 }, { "ISO-8859-7", 28597 }, { "ISO-8859-8", 28598 }, { "ISO-8859-9", 28599 }, { "ISO-8859-13", 28603 }, { "ISO-8859-15", 28605 }, { "ISO-2022-JP", 50222 }, { "CSISO2022JP", 50221 }, { "ISO-2022-KR", 50225 }, { "EUC-JP", 51932 }, { "EUC-CN", 51936 }, { "EUC-KR", 51949 }, { "GB18030", 54936 }, { "UTF-7", 65000 }, { "UTF-8", 65001 } }; static std::string stringToUpper (const std::string &str) { std::string result(str.size(), ' '); std::transform(str.cbegin(), str.cend(), result.begin(), ::toupper); return result; } static char *convertFromTo (const char *str, const char *from, const char *to) { if (!from || !to) return NULL; if (strcasecmp(from, to) == 0) return bctbx_strdup(str); char* convertedStr; int nChar, nbByte; LPWSTR wideStr; UINT rFrom, rTo; try { rFrom = windowsCharset.at(stringToUpper(std::string(from))); rTo = windowsCharset.at(stringToUpper(std::string(to))); } catch (const std::out_of_range&) { bctbx_error("Error while converting a string from '%s' to '%s': unknown charset", from, to); return NULL; } nChar = MultiByteToWideChar(rFrom, 0, str, -1, NULL, 0); if (nChar == 0) return NULL; wideStr = (LPWSTR) bctbx_malloc(nChar*sizeof(wideStr[0])); if (wideStr == NULL) return NULL; nChar = MultiByteToWideChar(rFrom, 0, str, -1, wideStr, nChar); if (nChar == 0) { bctbx_free(wideStr); wideStr = 0; } nbByte = WideCharToMultiByte(rTo, 0, wideStr, -1, 0, 0, 0, 0); if (nbByte == 0) return NULL; convertedStr = (char *) bctbx_malloc(nbByte); if (convertedStr == NULL) return NULL; nbByte = WideCharToMultiByte(rTo, 0, wideStr, -1, convertedStr, nbByte, 0, 0); if (nbByte == 0) { bctbx_free(convertedStr); convertedStr = 0; } bctbx_free(wideStr); return convertedStr; } char *bctbx_locale_to_utf8 (const char *str) { const char *defaultEncoding = bctbx_get_default_encoding(); if (!strcmp(defaultEncoding, "UTF-8")) return bctbx_strdup(str); return convertFromTo(str, defaultEncoding, "UTF-8"); } char *bctbx_utf8_to_locale (const char *str) { const char *defaultEncoding = bctbx_get_default_encoding(); if (!strcmp(defaultEncoding, "UTF-8")) return bctbx_strdup(str); return convertFromTo(str, "UTF-8", defaultEncoding); } char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) { return convertFromTo(str, encoding, "UTF-8"); } wchar_t* bctbx_string_to_wide_string(const char* str){ std::string s(str); int len; int slength = (int)s.length() + 1; len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); wchar_t* buf = (wchar_t *) bctbx_malloc(len*sizeof(wchar_t)); MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len); return buf; } unsigned int bctbx_get_code_page(const char* encoding){ unsigned int codePage = CP_ACP; std::string encodingStr; if(!encoding || encoding[0] == '\0') encodingStr = bctbx_get_default_encoding(); else encodingStr = encoding; try { codePage = windowsCharset.at(stringToUpper(encodingStr)); }catch (const std::out_of_range&) { bctbx_error("No code page found for '%s'. Using Locale.", encodingStr.c_str()); return CP_ACP; } return codePage; } bctoolbox-5.2.0/src/crypto/000077500000000000000000000000001434566643100156345ustar00rootroot00000000000000bctoolbox-5.2.0/src/crypto/crypto.c000066400000000000000000000105571434566643100173300ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /** * List of available key agreement algorithm */ uint32_t bctbx_key_agreement_algo_list(void) { uint32_t ret = BCTBX_DHM_2048|BCTBX_DHM_3072; /* provided by mbedtls */ #ifdef HAVE_DECAF /* decaf always provide X448 and X25519 */ ret |= BCTBX_ECDH_X25519 |BCTBX_ECDH_X448; #endif /* HAVE_DECAF */ return ret; } /*****************************************************************************/ /***** AES GCM encrypt/decrypt chunk by chunk, needed for file encryption ****/ /*****************************************************************************/ /** * @brief encrypt the file in input buffer for linphone encrypted file transfer * * @param[in/out] cryptoContext a context already initialized using bctbx_aes_gcm_context_new * @param[in] key encryption key * @param[in] length buffer size * @param[in] plain buffer holding the input data * @param[out] cipher buffer to store the output data */ int bctbx_aes_gcm_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { bctbx_aes_gcm_context_t *gcmContext; if (*cryptoContext == NULL && key == NULL) return -1; // we need the key, at least at first call if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */ /* key contains 192bits of key || 64 bits of Initialisation Vector, no additional data */ gcmContext = bctbx_aes_gcm_context_new(key, 24, NULL, 0, key+24, 8, BCTBX_GCM_ENCRYPT); if (gcmContext == NULL) { return -1; } *cryptoContext = gcmContext; } else { /* this is not the first call, get the context */ gcmContext = (bctbx_aes_gcm_context_t *)*cryptoContext; } if (plain != NULL) { bctbx_aes_gcm_process_chunk(gcmContext, (const uint8_t *)plain, length, (uint8_t *)cipher); } else { /* plain is NULL, finish the stream, if cipher is not null, generate a tag in it */ if (cipher != NULL && length > 0) { bctbx_aes_gcm_finish(gcmContext, (uint8_t *)cipher, length); } else { bctbx_aes_gcm_finish(gcmContext, NULL, 0); } *cryptoContext = NULL; } return 0; } /** * @brief decrypt the file in input buffer for linphone encrypted file transfer * * @param[in/out] cryptoContext a context already initialized using bctbx_aes_gcm_context_new * @param[in] key encryption key * @param[in] length buffer size * @param[out] plain buffer holding the output data * @param[int] cipher buffer to store the input data */ int bctbx_aes_gcm_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { bctbx_aes_gcm_context_t *gcmContext; if (*cryptoContext == NULL && key == NULL) return -1; // we need the key, at least at first call if (*cryptoContext == NULL) { /* first call to the function, allocate a crypto context and initialise it */ /* key contains 192bits of key || 64 bits of Initialisation Vector, no additional data */ gcmContext = bctbx_aes_gcm_context_new(key, 24, NULL, 0, key+24, 8, BCTBX_GCM_DECRYPT); if (gcmContext == NULL) { return -1; } *cryptoContext = gcmContext; } else { /* this is not the first call, get the context */ gcmContext = (bctbx_aes_gcm_context_t *)*cryptoContext; } if (cipher != NULL) { bctbx_aes_gcm_process_chunk(gcmContext, (const uint8_t *)cipher, length, (uint8_t *)plain); } else { /* cipher is NULL, finish the stream, if plain is not null and we have a length, compute the authentication tag*/ if (plain != NULL && length > 0) { bctbx_aes_gcm_finish(gcmContext, (uint8_t *)plain, length); } else { bctbx_aes_gcm_finish(gcmContext, NULL, 0); } *cryptoContext = NULL; } return 0; } bctoolbox-5.2.0/src/crypto/ecc.cc000066400000000000000000000541351434566643100167050ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_DECAF #include "decaf.h" #include "decaf/ed255.h" #include "decaf/ed448.h" int bctbx_crypto_have_ecc(void) { /* Check our re-defines of key length are matching the decaf ones */ static_assert(BCTBX_ECDH_X25519_PUBLIC_SIZE == DECAF_X25519_PUBLIC_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_ECDH_X25519_PRIVATE_SIZE == DECAF_X25519_PRIVATE_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_ECDH_X448_PUBLIC_SIZE == DECAF_X448_PUBLIC_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_ECDH_X448_PRIVATE_SIZE == DECAF_X448_PRIVATE_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_25519_PUBLIC_SIZE == DECAF_EDDSA_25519_PUBLIC_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_25519_PRIVATE_SIZE == DECAF_EDDSA_25519_PRIVATE_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_25519_SIGNATURE_SIZE == DECAF_EDDSA_25519_SIGNATURE_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_448_PUBLIC_SIZE == DECAF_EDDSA_448_PUBLIC_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_448_PRIVATE_SIZE == DECAF_EDDSA_448_PRIVATE_BYTES, "forwarding DECAF defines mismatch"); static_assert(BCTBX_EDDSA_448_SIGNATURE_SIZE == DECAF_EDDSA_448_SIGNATURE_BYTES, "forwarding DECAF defines mismatch"); return TRUE; } /*****************************************************************************/ /*** Elliptic Curve Diffie-Hellman - ECDH ***/ /*****************************************************************************/ /* Create and initialise the ECDH Context */ bctbx_ECDHContext_t *bctbx_CreateECDHContext(const uint8_t ECDHAlgo) { /* create the context */ bctbx_ECDHContext_t *context = (bctbx_ECDHContext_t *)bctbx_malloc(sizeof(bctbx_ECDHContext_t)); memset (context, 0, sizeof(bctbx_ECDHContext_t)); /* initialise pointer to NULL to ensure safe call to free() when destroying context */ context->secret = NULL; context->sharedSecret = NULL; context->selfPublic = NULL; context->peerPublic = NULL; context->cryptoModuleData = NULL; /* decaf do not use any context for these operations */ /* set parameters in the context */ context->algo=ECDHAlgo; switch (ECDHAlgo) { case BCTBX_ECDH_X25519: context->pointCoordinateLength = DECAF_X25519_PUBLIC_BYTES; context->secretLength = DECAF_X25519_PRIVATE_BYTES; break; case BCTBX_ECDH_X448: context->pointCoordinateLength = DECAF_X448_PUBLIC_BYTES; context->secretLength = DECAF_X448_PRIVATE_BYTES; break; default: bctbx_free(context); return NULL; break; } return context; } /** * * @brief Set the given secret key in the ECDH context * * @param[in/out] context ECDH context, will store the given secret key if length is matching the pre-setted algo for this context * @param[in] secret The buffer holding the secret, is duplicated in the ECDH context * @param[in] secretLength Length of previous buffer, must match the algo type setted at context creation */ void bctbx_ECDHSetSecretKey(bctbx_ECDHContext_t *context, const uint8_t *secret, const size_t secretLength) { if (context!=NULL && context->secretLength==secretLength) { if (context->secret == NULL) { /* allocate a new buffer */ context->secret = (uint8_t *)bctbx_malloc(context->secretLength); } else { /* or make sure we wipe out the existing one */ bctbx_clean(context->secret, context->secretLength); } memcpy(context->secret, secret, secretLength); } } /** * * @brief Set the given self public key in the ECDH context * Warning: no check if it matches the private key value * * @param[in/out] context ECDH context, will store the given self public key if length is matching the pre-setted algo for this context * @param[in] selfPublic The buffer holding the self public key, is duplicated in the ECDH context * @param[in] selfPublicLength Length of previous buffer, must match the algo type setted at context creation */ void bctbx_ECDHSetSelfPublicKey(bctbx_ECDHContext_t *context, const uint8_t *selfPublic, const size_t selfPublicLength) { if (context!=NULL && context->pointCoordinateLength==selfPublicLength) { if (context->selfPublic == NULL) { context->selfPublic = (uint8_t *)bctbx_malloc(selfPublicLength); } memcpy(context->selfPublic, selfPublic, selfPublicLength); } } /** * * @brief Set the given peer public key in the ECDH context * * @param[in/out] context ECDH context, will store the given peer public key if length is matching the pre-setted algo for this context * @param[in] peerPublic The buffer holding the peer public key, is duplicated in the ECDH context * @param[in] peerPublicLength Length of previous buffer, must match the algo type setted at context creation */ void bctbx_ECDHSetPeerPublicKey(bctbx_ECDHContext_t *context, const uint8_t *peerPublic, const size_t peerPublicLength) { if (context!=NULL && context->pointCoordinateLength==peerPublicLength) { /* allocate public key buffer if needed */ if (context->peerPublic == NULL) { context->peerPublic = (uint8_t *)bctbx_malloc(peerPublicLength); } memcpy(context->peerPublic, peerPublic, peerPublicLength); } } /** * * @brief Derive the public key from the secret setted in context and using preselected algo, following RFC7748 * * @param[in/out] context The context holding algo setting and secret, used to store public key */ void bctbx_ECDHDerivePublicKey(bctbx_ECDHContext_t *context) { if (context!=NULL && context->secret!=NULL) { /* allocate public key buffer if needed */ if (context->selfPublic == NULL) { context->selfPublic = (uint8_t *)bctbx_malloc(context->pointCoordinateLength); } /* then generate the public value */ switch (context->algo) { case BCTBX_ECDH_X25519: decaf_x25519_derive_public_key(context->selfPublic, context->secret); break; case BCTBX_ECDH_X448: decaf_x448_derive_public_key(context->selfPublic, context->secret); break; default: break; } } } /* generate the random secret and compute the public value */ void bctbx_ECDHCreateKeyPair(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { if (context!=NULL) { /* first generate the random bytes of self secret and store it in context(do it directly instead of creating a temp buffer and calling SetSecretKey) */ if (context->secret == NULL) { /* allocate buffer if needed */ context->secret = (uint8_t *)bctbx_malloc(context->secretLength); } else { /* otherwise make sure we wipe out previous secret */ bctbx_clean(context->secret, context->secretLength); } rngFunction(rngContext, context->secret, context->secretLength); /* Then derive the public key */ bctbx_ECDHDerivePublicKey(context); } } /* compute secret - the ->peerPublic field of context must have been set before calling this function */ void bctbx_ECDHComputeSecret(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { if (context != NULL && context->secret!=NULL && context->peerPublic!=NULL) { if (context->sharedSecret == NULL) { /* allocate buffer if needed */ context->sharedSecret = (uint8_t *)bctbx_malloc(context->pointCoordinateLength); } else { /* otherwise make sure we wipe out previous secret */ bctbx_clean(context->sharedSecret, context->pointCoordinateLength); } switch (context->algo) { case BCTBX_ECDH_X25519: if (decaf_x25519(context->sharedSecret, context->peerPublic, context->secret)==DECAF_FAILURE) { bctbx_free(context->sharedSecret); bctbx_clean(context->sharedSecret, context->pointCoordinateLength); context->sharedSecret=NULL; } break; case BCTBX_ECDH_X448: if (decaf_x448(context->sharedSecret, context->peerPublic, context->secret)==DECAF_FAILURE) { bctbx_free(context->sharedSecret); bctbx_clean(context->sharedSecret, context->pointCoordinateLength); context->sharedSecret=NULL; } break; default: break; } } } /* clean DHM context */ void bctbx_DestroyECDHContext(bctbx_ECDHContext_t *context) { if (context!= NULL) { /* key and secret must be erased from memory and not just freed */ if (context->secret != NULL) { bctbx_clean(context->secret, context->secretLength); free(context->secret); context->secret=NULL; } free(context->selfPublic); context->selfPublic=NULL; if (context->sharedSecret != NULL) { bctbx_clean(context->sharedSecret, context->pointCoordinateLength); free(context->sharedSecret); context->sharedSecret=NULL; } free(context->peerPublic); context->peerPublic=NULL; free(context); } } /*****************************************************************************/ /*** Edwards Curve Digital Signature Algorithm - EdDSA ***/ /*****************************************************************************/ /* Create and initialise the EDDSA Context */ bctbx_EDDSAContext_t *bctbx_CreateEDDSAContext(uint8_t EDDSAAlgo) { /* create the context */ bctbx_EDDSAContext_t *context = (bctbx_EDDSAContext_t *)bctbx_malloc(sizeof(bctbx_EDDSAContext_t)); memset (context, 0, sizeof(bctbx_EDDSAContext_t)); /* initialise pointer to NULL to ensure safe call to free() when destroying context */ context->secretKey = NULL; context->publicKey = NULL; context->cryptoModuleData = NULL; /* decaf do not use any context for these operations */ /* set parameters in the context */ context->algo=EDDSAAlgo; switch (EDDSAAlgo) { case BCTBX_EDDSA_25519: context->pointCoordinateLength = DECAF_EDDSA_25519_PUBLIC_BYTES; context->secretLength = DECAF_EDDSA_25519_PRIVATE_BYTES; break; case BCTBX_EDDSA_448: context->pointCoordinateLength = DECAF_EDDSA_448_PUBLIC_BYTES; context->secretLength = DECAF_EDDSA_448_PRIVATE_BYTES; break; default: bctbx_free(context); return NULL; break; } return context; } /* generate the random secret and compute the public value */ void bctbx_EDDSACreateKeyPair(bctbx_EDDSAContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { /* first generate the random bytes of self secret and store it in context */ if (context->secretKey==NULL) { context->secretKey = (uint8_t *)bctbx_malloc(context->secretLength); } rngFunction(rngContext, context->secretKey, context->secretLength); /* then generate the public value */ bctbx_EDDSADerivePublicKey(context); } /* using the secret key present in context, derive the public one */ void bctbx_EDDSADerivePublicKey(bctbx_EDDSAContext_t *context) { /* check we have a context and it was set to get a public key matching the length of the given one */ if (context != NULL) { if (context->secretKey != NULL) { /* don't go further if we have no secret key in context */ if (context->publicKey == NULL) { /* delete existing key if any */ context->publicKey = (uint8_t *)bctbx_malloc(context->pointCoordinateLength); } /* then generate the public value */ switch (context->algo) { case BCTBX_EDDSA_25519: decaf_ed25519_derive_public_key(context->publicKey, context->secretKey); break; case BCTBX_EDDSA_448: decaf_ed448_derive_public_key(context->publicKey, context->secretKey); break; default: break; } } } } /* clean EDDSA context */ void bctbx_DestroyEDDSAContext(bctbx_EDDSAContext_t *context) { if (context!= NULL) { /* secretKey must be erased from memory and not just freed */ if (context->secretKey != NULL) { bctbx_clean(context->secretKey, context->secretLength); free(context->secretKey); } free(context->publicKey); free(context); } } /** * * @brief Sign the message given using private key and EdDSA algo set in context * * @param[in] context EDDSA context storing the algorithm to use(ed448 or ed25519) and the private key to use * @param[in] message The message to be signed * @param[in] messageLength Length of the message buffer * @param [in] associatedData A "context" for this signature of up to 255 bytes. * @param [in] associatedDataLength Length of the context. * @param[out] signature The signature * @param[in/out] signatureLength The size of the signature buffer as input, the size of the actual signature as output * */ void bctbx_EDDSA_sign(bctbx_EDDSAContext_t *context, const uint8_t *message, const size_t messageLength, const uint8_t *associatedData, const uint8_t associatedDataLength, uint8_t *signature, size_t *signatureLength) { if (context!=NULL) { switch (context->algo) { case BCTBX_EDDSA_25519: if (*signatureLength>=DECAF_EDDSA_25519_SIGNATURE_BYTES) { /* check the buffer is large enough to hold the signature */ decaf_eddsa_25519_keypair_t keypair; decaf_ed25519_derive_keypair(keypair, context->secretKey); decaf_ed25519_keypair_sign ( signature, keypair, message, messageLength, 0, associatedData, associatedDataLength); decaf_ed25519_keypair_destroy(keypair); *signatureLength=DECAF_EDDSA_25519_SIGNATURE_BYTES; return; } break; case BCTBX_EDDSA_448: if (*signatureLength>=DECAF_EDDSA_448_SIGNATURE_BYTES) { /* check the buffer is large enough to hold the signature */ decaf_eddsa_448_keypair_t keypair; decaf_ed448_derive_keypair(keypair, context->secretKey); decaf_ed448_keypair_sign ( signature, keypair, message, messageLength, 0, associatedData, associatedDataLength); decaf_ed448_keypair_destroy(keypair); *signatureLength=DECAF_EDDSA_448_SIGNATURE_BYTES; return; } break; default: break; } } *signatureLength=0; } /** * * @brief Set a public key in a EDDSA context to be used to verify messages signature * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) * @param[in] publicKey The public to store in context * @param[in] publicKeyLength The length of previous buffer */ void bctbx_EDDSA_setPublicKey(bctbx_EDDSAContext_t *context, const uint8_t *publicKey, size_t publicKeyLength) { /* check we have a context and it was set to get a public key matching the length of the given one */ if (context != NULL) { if (context->pointCoordinateLength == publicKeyLength) { /* allocate key buffer if needed */ if (context->publicKey == NULL) { context->publicKey = (uint8_t *)bctbx_malloc(publicKeyLength); } memcpy(context->publicKey, publicKey, publicKeyLength); } } } /** * * @brief Set a private key in a EDDSA context to be used to sign message * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) * @param[in] secretKey The secret to store in context * @param[in] secretKeyLength The length of previous buffer */ void bctbx_EDDSA_setSecretKey(bctbx_EDDSAContext_t *context, const uint8_t *secretKey, size_t secretKeyLength) { /* check we have a context and it was set to get a public key matching the length of the given one */ if (context != NULL) { if (context->secretLength == secretKeyLength) { /* allocate key buffer if needed */ if (context->secretKey == NULL) { context->secretKey = (uint8_t *)bctbx_malloc(secretKeyLength); } memcpy(context->secretKey, secretKey, secretKeyLength); } } } /** * * @brief Use the public key set in context to verify the given signature and message * * @param[in/out] context EDDSA context storing the algorithm to use(ed448 or ed25519) and public key * @param[in] message Message to verify * @param[in] messageLength Length of the message buffer * @param [in] associatedData A "context" for this signature of up to 255 bytes. * @param [in] associatedDataLength Length of the context. * @param[in] signature The signature * @param[in] signatureLength The size of the signature buffer * * @return BCTBX_VERIFY_SUCCESS or BCTBX_VERIFY_FAILED */ int bctbx_EDDSA_verify(bctbx_EDDSAContext_t *context, const uint8_t *message, size_t messageLength, const uint8_t *associatedData, const uint8_t associatedDataLength, const uint8_t *signature, size_t signatureLength) { int ret = BCTBX_VERIFY_FAILED; if (context!=NULL) { decaf_error_t retDecaf = DECAF_FAILURE; switch (context->algo) { case BCTBX_EDDSA_25519: if (signatureLength==DECAF_EDDSA_25519_SIGNATURE_BYTES) { /* check length of given signature */ retDecaf = decaf_ed25519_verify (signature, context->publicKey, message, messageLength, 0, associatedData, associatedDataLength); } break; case BCTBX_EDDSA_448: if (signatureLength==DECAF_EDDSA_448_SIGNATURE_BYTES) { /* check lenght of given signature */ retDecaf = decaf_ed448_verify (signature, context->publicKey, message, messageLength, 0, associatedData, associatedDataLength); } break; default: break; } if (retDecaf == DECAF_SUCCESS) { ret = BCTBX_VERIFY_SUCCESS; } } return ret; } /** * * @brief Convert a EDDSA private key to a ECDH private key * pass the EDDSA private key through the hash function used in EdDSA * * @param[in] ed Context holding the current private key to convert * @param[out] x Context to store the private key for x25519 key exchange */ void bctbx_EDDSA_ECDH_privateKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x) { if (ed!=NULL && x!=NULL && ed->secretKey!=NULL) { if (ed->algo == BCTBX_EDDSA_25519 && x->algo == BCTBX_ECDH_X25519) { /* allocate key buffer if needed */ if (x->secret==NULL) { x->secret = (uint8_t *)bctbx_malloc(x->secretLength); } decaf_ed25519_convert_private_key_to_x25519(x->secret, ed->secretKey); } else if (ed->algo == BCTBX_EDDSA_448 && x->algo == BCTBX_ECDH_X448) { /* allocate key buffer if needed */ if (x->secret==NULL) { x->secret = (uint8_t *)bctbx_malloc(x->secretLength); } decaf_ed448_convert_private_key_to_x448(x->secret, ed->secretKey); } } } /** * * @brief Convert a EDDSA public key to a ECDH public key * point conversion : montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p * * @param[in] ed Context holding the current public key to convert * @param[out] x Context to store the public key for x25519 key exchange * @param[in] isSelf Flag to decide where to store the public key in context: BCTBX_ECDH_ISPEER or BCTBX_ECDH_ISSELF */ void bctbx_EDDSA_ECDH_publicKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x, uint8_t isSelf) { if (ed!=NULL && x!=NULL && ed->publicKey!=NULL) { if (ed->algo == BCTBX_EDDSA_25519 && x->algo == BCTBX_ECDH_X25519) { if (isSelf==BCTBX_ECDH_ISPEER) { if (x->peerPublic==NULL) { x->peerPublic = (uint8_t *)bctbx_malloc(x->pointCoordinateLength); } decaf_ed25519_convert_public_key_to_x25519(x->peerPublic, ed->publicKey); } else { if (x->selfPublic==NULL) { x->selfPublic = (uint8_t *)bctbx_malloc(x->pointCoordinateLength); } decaf_ed25519_convert_public_key_to_x25519(x->selfPublic, ed->publicKey); } } else if (ed->algo == BCTBX_EDDSA_448 && x->algo == BCTBX_ECDH_X448) { if (isSelf==BCTBX_ECDH_ISPEER) { if (x->peerPublic==NULL) { x->peerPublic = (uint8_t *)bctbx_malloc(x->pointCoordinateLength); } decaf_ed448_convert_public_key_to_x448(x->peerPublic, ed->publicKey); } else { if (x->selfPublic==NULL) { x->selfPublic = (uint8_t *)bctbx_malloc(x->pointCoordinateLength); } decaf_ed448_convert_public_key_to_x448(x->selfPublic, ed->publicKey); } } } } #else /* HAVE_DECAF */ /* We do not have lib decaf, implement empty stubs */ int bctbx_crypto_have_ecc(void) { return FALSE;} bctbx_ECDHContext_t *bctbx_CreateECDHContext(const uint8_t ECDHAlgo) {return NULL;} void bctbx_ECDHCreateKeyPair(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) {return;} void bctbx_ECDHSetSecretKey(bctbx_ECDHContext_t *context, const uint8_t *secret, const size_t secretLength){return;} void bctbx_ECDHSetSelfPublicKey(bctbx_ECDHContext_t *context, const uint8_t *selfPublic, const size_t selfPublicLength){return;} void bctbx_ECDHSetPeerPublicKey(bctbx_ECDHContext_t *context, const uint8_t *peerPublic, const size_t peerPublicLength){return;} void bctbx_ECDHDerivePublicKey(bctbx_ECDHContext_t *context){return;} void bctbx_ECDHComputeSecret(bctbx_ECDHContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext){return;} void bctbx_DestroyECDHContext(bctbx_ECDHContext_t *context){return;} bctbx_EDDSAContext_t *bctbx_CreateEDDSAContext(uint8_t EDDSAAlgo) {return NULL;} void bctbx_EDDSACreateKeyPair(bctbx_EDDSAContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) {return;} void bctbx_EDDSADerivePublicKey(bctbx_EDDSAContext_t *context) {return;} void bctbx_DestroyEDDSAContext(bctbx_EDDSAContext_t *context) {return;} void bctbx_EDDSA_sign(bctbx_EDDSAContext_t *context, const uint8_t *message, const size_t messageLength, const uint8_t *associatedData, const uint8_t associatedDataLength, uint8_t *signature, size_t *signatureLength) {return;} void bctbx_EDDSA_setPublicKey(bctbx_EDDSAContext_t *context, const uint8_t *publicKey, const size_t publicKeyLength) {return;} void bctbx_EDDSA_setSecretKey(bctbx_EDDSAContext_t *context, const uint8_t *secretKey, const size_t secretKeyLength) {return;} int bctbx_EDDSA_verify(bctbx_EDDSAContext_t *context, const uint8_t *message, size_t messageLength, const uint8_t *associatedData, const uint8_t associatedDataLength, const uint8_t *signature, size_t signatureLength) {return BCTBX_VERIFY_FAILED;} void bctbx_EDDSA_ECDH_privateKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x) {return;} void bctbx_EDDSA_ECDH_publicKeyConversion(const bctbx_EDDSAContext_t *ed, bctbx_ECDHContext_t *x, uint8_t isSelf) {return;} #endif bctoolbox-5.2.0/src/crypto/mbedtls.c000066400000000000000000002121211434566643100174310ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if MBEDTLS_VERSION_NUMBER >= 0x02040000 // v2.4.0 #include #else #include #endif #include "bctoolbox/crypto.h" #include "bctoolbox/logging.h" /*** Cleaning ***/ /** * @brief force a buffer value to zero in a way that shall prevent the compiler from optimizing it out * * @param[in/out] buffer the buffer to be cleared * @param[in] size buffer size */ void bctbx_clean(void *buffer, size_t size) { #if MBEDTLS_VERSION_NUMBER >= 0x020A0000 // v2.10.0 mbedtls_platform_zeroize(buffer, size); #else volatile uint8_t *p = buffer; while(size--) *p++ = 0; #endif } /*** Error code translation ***/ void bctbx_strerror(int32_t error_code, char *buffer, size_t buffer_length) { if (error_code>0) { snprintf(buffer, buffer_length, "%s", "Invalid Error code"); return ; } /* mbedtls error code are all negatived and bas smaller than 0x0000F000 */ /* bctoolbox defined error codes are all in format -0x7XXXXXXX */ if (-error_code<0x00010000) { /* it's a mbedtls error code */ mbedtls_strerror(error_code, buffer, buffer_length); return; } snprintf(buffer, buffer_length, "%s [-0x%x]", "bctoolbox defined error code", -error_code); return; } /*** base64 ***/ int32_t bctbx_base64_encode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length) { size_t byte_written = 0; int ret = mbedtls_base64_encode(output, *output_length, &byte_written, input, input_length); *output_length = byte_written; if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } return ret; } int32_t bctbx_base64_decode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length) { size_t byte_written = 0; int ret = mbedtls_base64_decode(output, *output_length, &byte_written, input, input_length); *output_length = byte_written; if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } if (ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER) { return BCTBX_ERROR_INVALID_BASE64_INPUT; } return ret; } /*** signing key ***/ bctbx_signing_key_t *bctbx_signing_key_new(void) { mbedtls_pk_context *key = bctbx_malloc0(sizeof(mbedtls_pk_context)); mbedtls_pk_init(key); return (bctbx_signing_key_t *)key; } void bctbx_signing_key_free(bctbx_signing_key_t *key) { mbedtls_pk_free((mbedtls_pk_context *)key); bctbx_free(key); } char *bctbx_signing_key_get_pem(bctbx_signing_key_t *key) { char *pem_key; if (key == NULL) return NULL; pem_key = (char *)bctbx_malloc0(4096); mbedtls_pk_write_key_pem( (mbedtls_pk_context *)key, (unsigned char *)pem_key, 4096); return pem_key; } int32_t bctbx_signing_key_parse(bctbx_signing_key_t *key, const char *buffer, size_t buffer_length, const unsigned char *password, size_t password_length) { int err; err=mbedtls_pk_parse_key((mbedtls_pk_context *)key, (const unsigned char *)buffer, buffer_length, password, password_length); if (err<0) { char tmp[128]; mbedtls_strerror(err,tmp,sizeof(tmp)); bctbx_error("cannot parse public key because [%s]",tmp); return BCTBX_ERROR_UNABLE_TO_PARSE_KEY; } return 0; } int32_t bctbx_signing_key_parse_file(bctbx_signing_key_t *key, const char *path, const char *password) { int err; err=mbedtls_pk_parse_keyfile((mbedtls_pk_context *)key, path, password); if (err<0) { char tmp[128]; mbedtls_strerror(err,tmp,sizeof(tmp)); bctbx_error("cannot parse public key because [%s]",tmp); return BCTBX_ERROR_UNABLE_TO_PARSE_KEY; } return 0; } /*** Certificate ***/ char *bctbx_x509_certificates_chain_get_pem(const bctbx_x509_certificate_t *cert) { char *pem_certificate = NULL; size_t olen=0; pem_certificate = (char *)bctbx_malloc0(4096); mbedtls_pem_write_buffer("-----BEGIN CERTIFICATE-----\n", "-----END CERTIFICATE-----\n", ((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len, (unsigned char*)pem_certificate, 4096, &olen ); return pem_certificate; } bctbx_x509_certificate_t *bctbx_x509_certificate_new(void) { mbedtls_x509_crt *cert = bctbx_malloc0(sizeof(mbedtls_x509_crt)); mbedtls_x509_crt_init(cert); return (bctbx_x509_certificate_t *)cert; } void bctbx_x509_certificate_free(bctbx_x509_certificate_t *cert) { mbedtls_x509_crt_free((mbedtls_x509_crt *)cert); bctbx_free(cert); } int32_t bctbx_x509_certificate_get_info_string(char *buf, size_t size, const char *prefix, const bctbx_x509_certificate_t *cert) { return mbedtls_x509_crt_info(buf, size, prefix, (mbedtls_x509_crt *)cert); } int32_t bctbx_x509_certificate_parse_file(bctbx_x509_certificate_t *cert, const char *path) { return mbedtls_x509_crt_parse_file((mbedtls_x509_crt *)cert, path); } int32_t bctbx_x509_certificate_parse_path(bctbx_x509_certificate_t *cert, const char *path) { return mbedtls_x509_crt_parse_path((mbedtls_x509_crt *)cert, path); } int32_t bctbx_x509_certificate_parse(bctbx_x509_certificate_t *cert, const char *buffer, size_t buffer_length) { return mbedtls_x509_crt_parse((mbedtls_x509_crt *)cert, (const unsigned char *)buffer, buffer_length); } int32_t bctbx_x509_certificate_get_der_length(bctbx_x509_certificate_t *cert) { if (cert!=NULL) { return (int32_t)((mbedtls_x509_crt *)cert)->raw.len; } return 0; } int32_t bctbx_x509_certificate_get_der(bctbx_x509_certificate_t *cert, unsigned char *buffer, size_t buffer_length) { if (cert==NULL) { return BCTBX_ERROR_INVALID_CERTIFICATE; } if (((mbedtls_x509_crt *)cert)->raw.len>buffer_length-1) { /* check buffer size is ok, +1 for the NULL termination added at the end */ return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } memcpy(buffer, ((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len); buffer[((mbedtls_x509_crt *)cert)->raw.len] = '\0'; /* add a null termination char */ return 0; } int32_t bctbx_x509_certificate_get_subject_dn(const bctbx_x509_certificate_t *cert, char *dn, size_t dn_length) { if (cert==NULL) { return BCTBX_ERROR_INVALID_CERTIFICATE; } return mbedtls_x509_dn_gets(dn, dn_length, &(((mbedtls_x509_crt *)cert)->subject)); } bctbx_list_t *bctbx_x509_certificate_get_subjects(const bctbx_x509_certificate_t *cert){ bctbx_list_t *ret = NULL; if (cert != NULL) { mbedtls_x509_crt *mbedtls_cert = (mbedtls_x509_crt *)cert; // bctbx_x509_certificate_t is just a cast of mbedtls_x509_crt /* parse subjectAltName if any */ if( mbedtls_cert->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) { const mbedtls_x509_sequence *cur = &(mbedtls_cert->subject_alt_names); while (cur != NULL) { ret = bctbx_list_append(ret, bctbx_strndup((const char*)cur->buf.p, (int)cur->buf.len)); cur = cur->next; } } /* Add Subject CN */ const mbedtls_x509_name *subject = &(mbedtls_cert->subject); while (subject != NULL) { // Certificate should hold only one CN, but be permissive and parse several if they are in the certificate if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, &subject->oid ) == 0 ) { // subject holds all the distinguished name in asn1 format, get the CN only ret = bctbx_list_append(ret, bctbx_strndup((const char*)subject->val.p, (int)subject->val.len)); } subject = subject->next; } } return ret; } int32_t bctbx_x509_certificate_generate_selfsigned(const char *subject, bctbx_x509_certificate_t *certificate, bctbx_signing_key_t *pkey, char * pem, size_t pem_length) { mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; int ret; mbedtls_mpi serial; mbedtls_x509write_cert crt; char file_buffer[8192]; size_t file_buffer_len = 0; char formatted_subject[512]; /* subject may be a sip URL or linphone-dtls-default-identity, add CN= before it to make a valid name */ memcpy(formatted_subject, "CN=", 3); memcpy(formatted_subject+3, subject, strlen(subject)+1); /* +1 to get the \0 termination */ mbedtls_entropy_init( &entropy ); mbedtls_ctr_drbg_init(&ctr_drbg); if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ) ) != 0 ) { bctbx_error("Certificate generation can't init ctr_drbg: [-0x%x]", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* generate 3072 bits RSA public/private key */ if ( (ret = mbedtls_pk_setup( (mbedtls_pk_context *)pkey, mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) )) != 0) { bctbx_error("Certificate generation can't init pk_ctx: [-0x%x]", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( ( ret = mbedtls_rsa_gen_key( mbedtls_pk_rsa( *(mbedtls_pk_context *)pkey ), mbedtls_ctr_drbg_random, &ctr_drbg, 3072, 65537 ) ) != 0) { bctbx_error("Certificate generation can't generate rsa key: [-0x%x]", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* if there is no pem pointer, don't save the key in pem format */ if (pem!=NULL) { mbedtls_pk_write_key_pem((mbedtls_pk_context *)pkey, (unsigned char *)file_buffer, 4096); file_buffer_len = strlen(file_buffer); } /* generate the certificate */ mbedtls_x509write_crt_init( &crt ); mbedtls_x509write_crt_set_md_alg( &crt, MBEDTLS_MD_SHA256 ); mbedtls_mpi_init( &serial ); if ( (ret = mbedtls_mpi_read_string( &serial, 10, "1" ) ) != 0 ) { bctbx_error("Certificate generation can't read serial mpi: [-0x%x]", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } mbedtls_x509write_crt_set_subject_key( &crt, (mbedtls_pk_context *)pkey); mbedtls_x509write_crt_set_issuer_key( &crt, (mbedtls_pk_context *)pkey); if ( (ret = mbedtls_x509write_crt_set_subject_name( &crt, formatted_subject) ) != 0) { bctbx_error("Certificate generation can't set subject name: [-0x%x]", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( (ret = mbedtls_x509write_crt_set_issuer_name( &crt, formatted_subject) ) != 0) { bctbx_error("Certificate generation can't set issuer name: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( (ret = mbedtls_x509write_crt_set_serial( &crt, &serial ) ) != 0) { bctbx_error("Certificate generation can't set serial: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } mbedtls_mpi_free(&serial); if ( (ret = mbedtls_x509write_crt_set_validity( &crt, "20010101000000", "20300101000000" ) ) != 0) { bctbx_error("Certificate generation can't set validity: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* store anyway certificate in pem format in a string even if we do not have file to write as we need it to get it in a x509_crt structure */ if ( (ret = mbedtls_x509write_crt_pem( &crt, (unsigned char *)file_buffer+file_buffer_len, 4096, mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0) { bctbx_error("Certificate generation can't write crt pem: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_WRITE_PEM; } mbedtls_x509write_crt_free(&crt); mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); /* copy the key+cert in pem format into the given buffer */ if (pem != NULL) { if (strlen(file_buffer)+1>pem_length) { bctbx_error("Certificate generation can't copy the certificate to pem buffer: too short [%ld] but need [%ld] bytes", (long)pem_length, (long)strlen(file_buffer)); return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } strncpy(pem, file_buffer, pem_length); } /* +1 on strlen as crt_parse in PEM format, length must include the final \0 */ if ( (ret = mbedtls_x509_crt_parse((mbedtls_x509_crt *)certificate, (unsigned char *)file_buffer, strlen(file_buffer)+1) ) != 0) { bctbx_error("Certificate generation can't parse crt pem: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_PARSE_PEM; } return 0; } int32_t bctbx_x509_certificate_get_signature_hash_function(const bctbx_x509_certificate_t *certificate, bctbx_md_type_t *hash_algorithm) { mbedtls_x509_crt *crt; if (certificate == NULL) return BCTBX_ERROR_INVALID_CERTIFICATE; crt = (mbedtls_x509_crt *)certificate; switch (crt->sig_md) { case MBEDTLS_MD_SHA1: *hash_algorithm = BCTBX_MD_SHA1; break; case MBEDTLS_MD_SHA224: *hash_algorithm = BCTBX_MD_SHA224; break; case MBEDTLS_MD_SHA256: *hash_algorithm = BCTBX_MD_SHA256; break; case MBEDTLS_MD_SHA384: *hash_algorithm = BCTBX_MD_SHA384; break; case MBEDTLS_MD_SHA512: *hash_algorithm = BCTBX_MD_SHA512; break; default: *hash_algorithm = BCTBX_MD_UNDEFINED; return BCTBX_ERROR_UNSUPPORTED_HASH_FUNCTION; break; } return 0; } /* maximum length of returned buffer will be 7(SHA-512 string)+3*hash_length(64)+null char = 200 bytes */ int32_t bctbx_x509_certificate_get_fingerprint(const bctbx_x509_certificate_t *certificate, char *fingerprint, size_t fingerprint_length, bctbx_md_type_t hash_algorithm) { unsigned char buffer[64]={0}; /* buffer is max length of returned hash, which is 64 in case we use sha-512 */ size_t hash_length = 0; const char *hash_alg_string=NULL; size_t fingerprint_size = 0; mbedtls_x509_crt *crt; mbedtls_md_type_t hash_id; if (certificate == NULL) return BCTBX_ERROR_INVALID_CERTIFICATE; crt = (mbedtls_x509_crt *)certificate; /* if there is a specified hash algorithm, use it*/ switch (hash_algorithm) { case BCTBX_MD_SHA1: hash_id = MBEDTLS_MD_SHA1; break; case BCTBX_MD_SHA224: hash_id = MBEDTLS_MD_SHA224; break; case BCTBX_MD_SHA256: hash_id = MBEDTLS_MD_SHA256; break; case BCTBX_MD_SHA384: hash_id = MBEDTLS_MD_SHA384; break; case BCTBX_MD_SHA512: hash_id = MBEDTLS_MD_SHA512; break; default: /* nothing specified, use the hash algo used in the certificate signature */ hash_id = crt->sig_md; break; } /* fingerprint is a hash of the DER formated certificate (found in crt->raw.p) using the same hash function used by certificate signature */ switch (hash_id) { case MBEDTLS_MD_SHA1: mbedtls_sha1(crt->raw.p, crt->raw.len, buffer); hash_length = 20; hash_alg_string="SHA-1"; break; case MBEDTLS_MD_SHA224: mbedtls_sha256(crt->raw.p, crt->raw.len, buffer, 1); /* last argument is a boolean, indicate to output sha-224 and not sha-256 */ hash_length = 28; hash_alg_string="SHA-224"; break; case MBEDTLS_MD_SHA256: mbedtls_sha256(crt->raw.p, crt->raw.len, buffer, 0); hash_length = 32; hash_alg_string="SHA-256"; break; case MBEDTLS_MD_SHA384: mbedtls_sha512(crt->raw.p, crt->raw.len, buffer, 1); /* last argument is a boolean, indicate to output sha-384 and not sha-512 */ hash_length = 48; hash_alg_string="SHA-384"; break; case MBEDTLS_MD_SHA512: mbedtls_sha512(crt->raw.p, crt->raw.len, buffer, 0); hash_length = 64; hash_alg_string="SHA-512"; break; default: return BCTBX_ERROR_UNSUPPORTED_HASH_FUNCTION; break; } if (hash_length>0) { size_t i; size_t fingerprint_index = strlen(hash_alg_string); char prefix=' '; fingerprint_size=fingerprint_index+3*hash_length+1; /* fingerprint will be : hash_alg_string+' '+HEX : separated values: length is strlen(hash_alg_string)+3*hash_lenght + 1 for null termination */ if (fingerprint_lengthbuffer_size) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } strncpy(buffer, outputString, buffer_size); return 0; } int32_t bctbx_x509_certificate_set_flag(uint32_t *flags, uint32_t flags_to_set) { if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED) *flags |= MBEDTLS_X509_BADCERT_EXPIRED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED) *flags |= MBEDTLS_X509_BADCERT_REVOKED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH) *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED) *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING) *flags |= MBEDTLS_X509_BADCERT_MISSING; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_SKIP_VERIFY) *flags |= MBEDTLS_X509_BADCERT_SKIP_VERIFY; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_OTHER) *flags |= MBEDTLS_X509_BADCERT_OTHER; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_FUTURE) *flags |= MBEDTLS_X509_BADCERT_FUTURE; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_KEY_USAGE) *flags |= MBEDTLS_X509_BADCERT_KEY_USAGE; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXT_KEY_USAGE) *flags |= MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_NS_CERT_TYPE) *flags |= MBEDTLS_X509_BADCERT_NS_CERT_TYPE; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_MD) *flags |= MBEDTLS_X509_BADCERT_BAD_MD; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_PK) *flags |= MBEDTLS_X509_BADCERT_BAD_PK; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_KEY) *flags |= MBEDTLS_X509_BADCERT_BAD_KEY; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED) *flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED) *flags |= MBEDTLS_X509_BADCRL_EXPIRED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_FUTURE) *flags |= MBEDTLS_X509_BADCRL_FUTURE; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_MD) *flags |= MBEDTLS_X509_BADCRL_BAD_MD; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_PK) *flags |= MBEDTLS_X509_BADCRL_BAD_PK; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_KEY) *flags |= MBEDTLS_X509_BADCRL_BAD_KEY; return 0; } uint32_t bctbx_x509_certificate_remap_flag(uint32_t flags) { uint32_t ret = 0; if (flags & MBEDTLS_X509_BADCERT_EXPIRED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED; if (flags & MBEDTLS_X509_BADCERT_REVOKED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED; if (flags & MBEDTLS_X509_BADCERT_CN_MISMATCH) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH; if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED; if (flags & MBEDTLS_X509_BADCERT_MISSING) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING; if (flags & MBEDTLS_X509_BADCERT_SKIP_VERIFY) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_SKIP_VERIFY; if (flags & MBEDTLS_X509_BADCERT_FUTURE) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_FUTURE; if (flags & MBEDTLS_X509_BADCERT_OTHER) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_OTHER; if (flags & MBEDTLS_X509_BADCERT_KEY_USAGE) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_KEY_USAGE; if (flags & MBEDTLS_X509_BADCERT_EXT_KEY_USAGE) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_EXT_KEY_USAGE; if (flags & MBEDTLS_X509_BADCERT_NS_CERT_TYPE) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_NS_CERT_TYPE; if (flags & MBEDTLS_X509_BADCERT_BAD_MD) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_MD; if (flags & MBEDTLS_X509_BADCERT_BAD_PK) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_PK; if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_KEY; if (flags & MBEDTLS_X509_BADCRL_NOT_TRUSTED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED; if (flags & MBEDTLS_X509_BADCRL_EXPIRED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED; if (flags & MBEDTLS_X509_BADCRL_FUTURE) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_FUTURE; if (flags & MBEDTLS_X509_BADCRL_BAD_MD) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_MD; if (flags & MBEDTLS_X509_BADCRL_BAD_PK) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_PK; if (flags & MBEDTLS_X509_BADCRL_BAD_KEY) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_KEY; return ret; } int32_t bctbx_x509_certificate_unset_flag(uint32_t *flags, uint32_t flags_to_unset) { if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED) *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED) *flags &= ~MBEDTLS_X509_BADCERT_REVOKED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH) *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED) *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING) *flags &= ~MBEDTLS_X509_BADCERT_MISSING; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_SKIP_VERIFY) *flags &= ~MBEDTLS_X509_BADCERT_SKIP_VERIFY; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_OTHER) *flags &= ~MBEDTLS_X509_BADCERT_OTHER; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_FUTURE) *flags &= ~MBEDTLS_X509_BADCERT_FUTURE; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_KEY_USAGE) *flags &= ~MBEDTLS_X509_BADCERT_KEY_USAGE; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXT_KEY_USAGE) *flags &= ~MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_NS_CERT_TYPE) *flags &= ~MBEDTLS_X509_BADCERT_NS_CERT_TYPE; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_MD) *flags &= ~MBEDTLS_X509_BADCERT_BAD_MD; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_PK) *flags &= ~MBEDTLS_X509_BADCERT_BAD_PK; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_BAD_KEY) *flags &= ~MBEDTLS_X509_BADCERT_BAD_KEY; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED) *flags &= ~MBEDTLS_X509_BADCRL_NOT_TRUSTED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED) *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_FUTURE) *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_MD) *flags &= ~MBEDTLS_X509_BADCRL_BAD_MD; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_PK) *flags &= ~MBEDTLS_X509_BADCRL_BAD_PK; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_BAD_KEY) *flags &= ~MBEDTLS_X509_BADCRL_BAD_KEY; return 0; } /*** Diffie-Hellman-Merkle ***/ /* initialise de DHM context according to requested algorithm */ bctbx_DHMContext_t *bctbx_CreateDHMContext(uint8_t DHMAlgo, uint8_t secretLength) { mbedtls_dhm_context *mbedtlsDhmContext; /* create the context */ bctbx_DHMContext_t *context = (bctbx_DHMContext_t *)malloc(sizeof(bctbx_DHMContext_t)); memset (context, 0, sizeof(bctbx_DHMContext_t)); /* create the mbedtls context for DHM */ mbedtlsDhmContext=(mbedtls_dhm_context *)malloc(sizeof(mbedtls_dhm_context)); memset(mbedtlsDhmContext, 0, sizeof(mbedtls_dhm_context)); context->cryptoModuleData=(void *)mbedtlsDhmContext; /* initialise pointer to NULL to ensure safe call to free() when destroying context */ context->secret = NULL; context->self = NULL; context->key = NULL; context->peer = NULL; /* set parameters in the context */ context->algo=DHMAlgo; context->secretLength = secretLength; switch (DHMAlgo) { case BCTBX_DHM_2048: /* set P and G in the mbedtls context */ if ((mbedtls_mpi_read_string(&(mbedtlsDhmContext->P), 16, MBEDTLS_DHM_RFC3526_MODP_2048_P) != 0) || (mbedtls_mpi_read_string(&(mbedtlsDhmContext->G), 16, MBEDTLS_DHM_RFC3526_MODP_2048_G) != 0)) { return NULL; } context->primeLength=256; mbedtlsDhmContext->len=256; break; case BCTBX_DHM_3072: /* set P and G in the mbedtls context */ if ((mbedtls_mpi_read_string(&(mbedtlsDhmContext->P), 16, MBEDTLS_DHM_RFC3526_MODP_3072_P) != 0) || (mbedtls_mpi_read_string(&(mbedtlsDhmContext->G), 16, MBEDTLS_DHM_RFC3526_MODP_3072_G) != 0)) { return NULL; } context->primeLength=384; mbedtlsDhmContext->len=384; break; default: free(context); return NULL; break; } return context; } /* generate the random secret and compute the public value */ void bctbx_DHMCreatePublic(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { /* get the mbedtls context */ mbedtls_dhm_context *mbedtlsContext = (mbedtls_dhm_context *)context->cryptoModuleData; /* allocate output buffer */ context->self = (uint8_t *)malloc(context->primeLength*sizeof(uint8_t)); mbedtls_dhm_make_public(mbedtlsContext, context->secretLength, context->self, context->primeLength, (int (*)(void *, unsigned char *, size_t))rngFunction, rngContext); } /* compute secret - the ->peer field of context must have been set before calling this function */ void bctbx_DHMComputeSecret(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { size_t keyLength; uint8_t sharedSecretBuffer[384]; /* longest shared secret available in these mode */ /* import the peer public value G^Y mod P in the mbedtls dhm context */ mbedtls_dhm_read_public((mbedtls_dhm_context *)(context->cryptoModuleData), context->peer, context->primeLength); /* compute the secret key */ keyLength = context->primeLength; /* undocumented but this value seems to be in/out, so we must set it to the expected key length */ context->key = (uint8_t *)malloc(keyLength*sizeof(uint8_t)); /* allocate and reset the key buffer */ memset(context->key,0, keyLength); mbedtls_dhm_calc_secret((mbedtls_dhm_context *)(context->cryptoModuleData), sharedSecretBuffer, 384, &keyLength, (int (*)(void *, unsigned char *, size_t))rngFunction, rngContext); /* now copy the resulting secret in the correct place in buffer(result may actually miss some front zero bytes, real length of output is now in keyLength but we want primeLength bytes) */ memcpy(context->key+(context->primeLength-keyLength), sharedSecretBuffer, keyLength); bctbx_clean(sharedSecretBuffer, 384); /* purge secret from temporary buffer */ } /* clean DHM context */ void bctbx_DestroyDHMContext(bctbx_DHMContext_t *context) { if (context!= NULL) { /* key and secret must be erased from memory and not just freed */ if (context->secret != NULL) { bctbx_clean(context->secret, context->secretLength); free(context->secret); } free(context->self); if (context->key != NULL) { bctbx_clean(context->key, context->primeLength); free(context->key); } free(context->peer); mbedtls_dhm_free((mbedtls_dhm_context *)context->cryptoModuleData); free(context->cryptoModuleData); free(context); } } /*** SSL Client ***/ static int bctbx_ssl_sendrecv_callback_return_remap(int32_t ret_code) { switch (ret_code) { case BCTBX_ERROR_NET_WANT_READ: return MBEDTLS_ERR_SSL_WANT_READ; case BCTBX_ERROR_NET_WANT_WRITE: return MBEDTLS_ERR_SSL_WANT_WRITE; case BCTBX_ERROR_NET_CONN_RESET: return MBEDTLS_ERR_NET_CONN_RESET; default: return (int)ret_code; } } /* * Default profile used to configure ssl_context, allow 1024 bits keys(while mbedtls default is 2048) */ const mbedtls_x509_crt_profile bctbx_x509_crt_profile_default = { /* Hashes from SHA-1 and above */ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) | MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), 0xFFFFFFF, /* Any PK alg */ 0xFFFFFFF, /* Any curve */ 1024, }; /** context **/ struct bctbx_ssl_context_struct { mbedtls_ssl_context ssl_ctx; int(*callback_cli_cert_function)(void *, bctbx_ssl_context_t *, const bctbx_list_t *); /**< pointer to the callback called to update client certificate during handshake callback params are user_data, ssl_context, list of server certificate subject alt name and CN (null terminated strings) */ void *callback_cli_cert_data; /**< data passed to the client cert callback */ int(*callback_send_function)(void *, const unsigned char *, size_t); /* callbacks args are: callback data, data buffer to be send, size of data buffer */ int(*callback_recv_function)(void *, unsigned char *, size_t); /* args: callback data, data buffer to be read, size of data buffer */ void *callback_sendrecv_data; /**< data passed to send/recv callbacks */ mbedtls_timing_delay_context timer; /**< a timer is requested for DTLS */ }; bctbx_ssl_context_t *bctbx_ssl_context_new(void) { bctbx_ssl_context_t *ssl_ctx = bctbx_malloc0(sizeof(bctbx_ssl_context_t)); mbedtls_ssl_init(&(ssl_ctx->ssl_ctx)); ssl_ctx->callback_cli_cert_function = NULL; ssl_ctx->callback_cli_cert_data = NULL; ssl_ctx->callback_send_function = NULL; ssl_ctx->callback_recv_function = NULL; ssl_ctx->callback_sendrecv_data = NULL; return ssl_ctx; } void bctbx_ssl_context_free(bctbx_ssl_context_t *ssl_ctx) { mbedtls_ssl_free(&(ssl_ctx->ssl_ctx)); bctbx_free(ssl_ctx); } int32_t bctbx_ssl_close_notify(bctbx_ssl_context_t *ssl_ctx) { return mbedtls_ssl_close_notify(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_session_reset(bctbx_ssl_context_t *ssl_ctx) { return mbedtls_ssl_session_reset(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_write(bctbx_ssl_context_t *ssl_ctx, const unsigned char *buf, size_t buf_length) { int ret = mbedtls_ssl_write(&(ssl_ctx->ssl_ctx), buf, buf_length); /* remap some output code */ if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = BCTBX_ERROR_NET_WANT_WRITE; } return ret; } int32_t bctbx_ssl_read(bctbx_ssl_context_t *ssl_ctx, unsigned char *buf, size_t buf_length) { int ret = mbedtls_ssl_read(&(ssl_ctx->ssl_ctx), buf, buf_length); /* remap some output code */ if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { ret = BCTBX_ERROR_SSL_PEER_CLOSE_NOTIFY; } if (ret == MBEDTLS_ERR_SSL_WANT_READ) { ret = BCTBX_ERROR_NET_WANT_READ; } return ret; } int32_t bctbx_ssl_handshake(bctbx_ssl_context_t *ssl_ctx) { int ret = 0; while( ssl_ctx->ssl_ctx.state != MBEDTLS_SSL_HANDSHAKE_OVER ) { ret = mbedtls_ssl_handshake_step(&(ssl_ctx->ssl_ctx)); if( ret != 0 ) { break; } /* insert the callback function for client certificate request */ if (ssl_ctx->callback_cli_cert_function != NULL) { /* check we have a callback function */ /* when in state SSL_CLIENT_CERTIFICATE - which means, next call to ssl_handshake_step will send the client certificate to server - * and the client_auth flag is set - which means the server requested a client certificate - */ if (ssl_ctx->ssl_ctx.state == MBEDTLS_SSL_CLIENT_CERTIFICATE && ssl_ctx->ssl_ctx.client_auth > 0) { /* Retrieve peer certificate subject altname and cn during handshake from server certificate request * Get the peer certificate from mbedtls ssl context. No accessor to get it, * fetch it directly from session_negociate which holds the currently negotiated handshake */ bctbx_list_t *names = bctbx_x509_certificate_get_subjects((const bctbx_x509_certificate_t *)ssl_ctx->ssl_ctx.session_negotiate->peer_cert); if (ssl_ctx->callback_cli_cert_function(ssl_ctx->callback_cli_cert_data, ssl_ctx, names)!=0) { bctbx_list_free_with_data(names, bctbx_free); if((ret=mbedtls_ssl_send_alert_message(&(ssl_ctx->ssl_ctx), MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE)) != 0 ) return( ret ); } bctbx_list_free_with_data(names, bctbx_free); } } } /* remap some output codes */ if (ret == MBEDTLS_ERR_SSL_WANT_READ) { ret = BCTBX_ERROR_NET_WANT_READ; } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { ret = BCTBX_ERROR_NET_WANT_WRITE; } return(ret); } int32_t bctbx_ssl_set_hs_own_cert(bctbx_ssl_context_t *ssl_ctx, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key) { /* WARNING Dirty-Dirty trick : in mbedtls ssl_set_hs_own_cert shall be caller on server side only for SNI, there is no real equivalent on client side yet */ /* If we are server call the regular function */ if (ssl_ctx->ssl_ctx.conf->endpoint == MBEDTLS_SSL_IS_SERVER ) { return mbedtls_ssl_set_hs_own_cert(&(ssl_ctx->ssl_ctx) , (mbedtls_x509_crt *)cert , (mbedtls_pk_context *)key); } else { /* if we are client, call the conf function on the function in use inside the ssl_context, dirty but it works for now */ return mbedtls_ssl_conf_own_cert((mbedtls_ssl_config *)ssl_ctx->ssl_ctx.conf , (mbedtls_x509_crt *)cert , (mbedtls_pk_context *)key); } } int bctbx_ssl_send_callback(void *data, const unsigned char *buffer, size_t buffer_length) { int ret = 0; /* data is the ssl_context which contains the actual callback and data */ bctbx_ssl_context_t *ssl_ctx = (bctbx_ssl_context_t *)data; ret = ssl_ctx->callback_send_function(ssl_ctx->callback_sendrecv_data, buffer, buffer_length); return bctbx_ssl_sendrecv_callback_return_remap(ret); } int bctbx_ssl_recv_callback(void *data, unsigned char *buffer, size_t buffer_length) { int ret = 0; /* data is the ssl_context which contains the actual callback and data */ bctbx_ssl_context_t *ssl_ctx = (bctbx_ssl_context_t *)data; ret = ssl_ctx->callback_recv_function(ssl_ctx->callback_sendrecv_data, buffer, buffer_length); return bctbx_ssl_sendrecv_callback_return_remap(ret); } void bctbx_ssl_set_io_callbacks(bctbx_ssl_context_t *ssl_ctx, void *callback_data, int(*callback_send_function)(void *, const unsigned char *, size_t), /* callbacks args are: callback data, data buffer to be send, size of data buffer */ int(*callback_recv_function)(void *, unsigned char *, size_t)){ /* args: callback data, data buffer to be read, size of data buffer */ if (ssl_ctx==NULL) { return; } ssl_ctx->callback_send_function = callback_send_function; ssl_ctx->callback_recv_function = callback_recv_function; ssl_ctx->callback_sendrecv_data = callback_data; mbedtls_ssl_set_bio(&(ssl_ctx->ssl_ctx), ssl_ctx, bctbx_ssl_send_callback, bctbx_ssl_recv_callback, NULL); } const bctbx_x509_certificate_t *bctbx_ssl_get_peer_certificate(bctbx_ssl_context_t *ssl_ctx) { return (const bctbx_x509_certificate_t *)mbedtls_ssl_get_peer_cert(&(ssl_ctx->ssl_ctx)); } const char *bctbx_ssl_get_ciphersuite(bctbx_ssl_context_t *ssl_ctx){ return mbedtls_ssl_get_ciphersuite(&(ssl_ctx->ssl_ctx)); } int bctbx_ssl_get_ciphersuite_id(const char *ciphersuite){ return mbedtls_ssl_get_ciphersuite_id(ciphersuite); } const char *bctbx_ssl_get_version(bctbx_ssl_context_t *ssl_ctx){ return mbedtls_ssl_get_version(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_set_hostname(bctbx_ssl_context_t *ssl_ctx, const char *hostname){ return mbedtls_ssl_set_hostname(&(ssl_ctx->ssl_ctx), hostname); } /** DTLS SRTP functions **/ #ifdef HAVE_DTLS_SRTP uint8_t bctbx_dtls_srtp_supported(void) { return 1; } void bctbx_ssl_set_mtu(bctbx_ssl_context_t *ssl_ctx, uint16_t mtu) { // remove all headers to the given MTU: // DTLS header (mbedtls_ssl_get_record_expansion) // UDP header: 8 bytes // IP header(up to 40 bytes when usimg IP v6) mbedtls_ssl_set_mtu(&(ssl_ctx->ssl_ctx), (mtu - mbedtls_ssl_get_record_expansion(&(ssl_ctx->ssl_ctx)) - 8 - 40)); } static bctbx_dtls_srtp_profile_t bctbx_srtp_profile_mbedtls2bctoolbox(mbedtls_ssl_srtp_profile mbedtls_profile) { switch (mbedtls_profile) { case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80: return BCTBX_SRTP_AES128_CM_HMAC_SHA1_80; case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32: return BCTBX_SRTP_AES128_CM_HMAC_SHA1_32; case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80: return BCTBX_SRTP_NULL_HMAC_SHA1_80; case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32: return BCTBX_SRTP_NULL_HMAC_SHA1_32; default: return BCTBX_SRTP_UNDEFINED; } } static mbedtls_ssl_srtp_profile bctbx_srtp_profile_bctoolbox2mbedtls(bctbx_dtls_srtp_profile_t bctbx_profile) { switch (bctbx_profile) { case BCTBX_SRTP_AES128_CM_HMAC_SHA1_80: return MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80; case BCTBX_SRTP_AES128_CM_HMAC_SHA1_32: return MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32; case BCTBX_SRTP_NULL_HMAC_SHA1_80: return MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80; case BCTBX_SRTP_NULL_HMAC_SHA1_32: return MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32; default: return MBEDTLS_TLS_SRTP_UNSET; } } bctbx_dtls_srtp_profile_t bctbx_ssl_get_dtls_srtp_protection_profile(bctbx_ssl_context_t *ssl_ctx) { if (ssl_ctx==NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } mbedtls_dtls_srtp_info dtls_srtp_negotiation_result; mbedtls_ssl_get_dtls_srtp_negotiation_result(&(ssl_ctx->ssl_ctx), &dtls_srtp_negotiation_result); return bctbx_srtp_profile_mbedtls2bctoolbox(dtls_srtp_negotiation_result.chosen_dtls_srtp_profile); }; #else /* HAVE_DTLS_SRTP */ /* dummy DTLS api when not available */ uint8_t bctbx_dtls_srtp_supported(void) { return 0; } void bctbx_ssl_set_mtu(bctbx_ssl_context_t *ssl_ctx, uint16_t mtu) { } bctbx_dtls_srtp_profile_t bctbx_ssl_get_dtls_srtp_protection_profile(bctbx_ssl_context_t *ssl_ctx) { return BCTBX_SRTP_UNDEFINED; } #endif /* HAVE_DTLS_SRTP */ /** DTLS SRTP functions **/ /** config **/ #ifdef HAVE_DTLS_SRTP typedef struct bctbx_dtls_srtp_keys { uint8_t master_secret[48]; // master secret generated during handshake uint8_t randoms[64]; // client || server randoms, 32 bytes each mbedtls_tls_prf_types tls_prf_type; // prf function identification } bctbx_dtls_srtp_keys_t; #endif /* HAVE_DTLS_SRTP */ struct bctbx_ssl_config_struct { mbedtls_ssl_config *ssl_config; /**< actual config structure */ uint8_t ssl_config_externally_provided; /**< a flag, on when the ssl_config was provided by callers and not created threw the new function */ int(*callback_cli_cert_function)(void *, bctbx_ssl_context_t *, const bctbx_list_t *); /**< pointer to the callback called to update client certificate during handshake callback params are user_data, ssl_context, list of server certificate subject alt name and CN (null terminated strings) */ void *callback_cli_cert_data; /**< data passed to the client cert callback */ #ifdef HAVE_DTLS_SRTP mbedtls_ssl_srtp_profile dtls_srtp_mbedtls_profiles[MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH + 1]; /**< list of supported DTLS-SRTP profiles, mbedtls won't hold the reference, so we must do it for the lifetime of the config structure. (size is +1 to add the list termination) */ bctbx_dtls_srtp_keys_t dtls_srtp_keys; /**< Key material is stored during the handshake there and used after completion to generate the DTLS-SRTP shared secret */ #endif /* HAVE_DTLS_SRTP */ }; bctbx_ssl_config_t *bctbx_ssl_config_new(void) { bctbx_ssl_config_t *ssl_config = bctbx_malloc0(sizeof(bctbx_ssl_config_t)); /* allocate and init anyway a ssl_config, it may be then crashed by an externally provided one */ ssl_config->ssl_config = bctbx_malloc0(sizeof(mbedtls_ssl_config)); ssl_config->ssl_config_externally_provided = 0; mbedtls_ssl_config_init(ssl_config->ssl_config); ssl_config->callback_cli_cert_function = NULL; ssl_config->callback_cli_cert_data = NULL; return ssl_config; } bctbx_type_implementation_t bctbx_ssl_get_implementation_type(void) { return BCTBX_MBEDTLS; } int32_t bctbx_ssl_config_set_crypto_library_config(bctbx_ssl_config_t *ssl_config, void *internal_config) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* free already existing structure */ if (ssl_config->ssl_config != NULL && ssl_config->ssl_config_externally_provided==0) { mbedtls_ssl_config_free(ssl_config->ssl_config); bctbx_free(ssl_config->ssl_config); } /* set the given pointer as the ssl_config context */ ssl_config->ssl_config = (mbedtls_ssl_config *)internal_config; /* set the flag in order to not free the mbedtls config when freing bctbx_ssl_config */ ssl_config->ssl_config_externally_provided = 1; return 0; } void bctbx_ssl_config_free(bctbx_ssl_config_t *ssl_config) { if (ssl_config == NULL) { return; } /* free mbedtls_ssl_config only when created internally */ if (ssl_config->ssl_config_externally_provided==0) { mbedtls_ssl_config_free(ssl_config->ssl_config); bctbx_free(ssl_config->ssl_config); } #ifdef HAVE_DTLS_SRTP bctbx_clean(ssl_config->dtls_srtp_keys.master_secret, sizeof(ssl_config->dtls_srtp_keys.master_secret)); bctbx_clean(ssl_config->dtls_srtp_keys.randoms, sizeof(ssl_config->dtls_srtp_keys.randoms)); #endif /* HAVE_DTLS_SRTP */ bctbx_free(ssl_config); } int32_t bctbx_ssl_config_defaults(bctbx_ssl_config_t *ssl_config, int endpoint, int transport) { int mbedtls_endpoint, mbedtls_transport, ret; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* remap input arguments */ switch (endpoint) { case BCTBX_SSL_IS_CLIENT: mbedtls_endpoint = MBEDTLS_SSL_IS_CLIENT; break; case BCTBX_SSL_IS_SERVER: mbedtls_endpoint = MBEDTLS_SSL_IS_SERVER; break; default: return BCTBX_ERROR_INVALID_INPUT_DATA; } switch (transport) { case BCTBX_SSL_TRANSPORT_STREAM: mbedtls_transport = MBEDTLS_SSL_TRANSPORT_STREAM; break; case BCTBX_SSL_TRANSPORT_DATAGRAM: mbedtls_transport = MBEDTLS_SSL_TRANSPORT_DATAGRAM; break; default: return BCTBX_ERROR_INVALID_INPUT_DATA; } ret = mbedtls_ssl_config_defaults(ssl_config->ssl_config, mbedtls_endpoint, mbedtls_transport, MBEDTLS_SSL_PRESET_DEFAULT); if (ret <0) { return ret; } if (transport == BCTBX_SSL_TRANSPORT_DATAGRAM) { // Set agressive repetition timer for DTLS handshake mbedtls_ssl_conf_handshake_timeout(ssl_config->ssl_config, 400, 15000); } /* Set the default x509 security profile used for verification of all certificate in chain */ mbedtls_ssl_conf_cert_profile(ssl_config->ssl_config, &bctbx_x509_crt_profile_default); return ret; } int32_t bctbx_ssl_config_set_endpoint(bctbx_ssl_config_t *ssl_config, int endpoint) { int mbedtls_endpoint; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* remap input arguments */ switch (endpoint) { case BCTBX_SSL_IS_CLIENT: mbedtls_endpoint = MBEDTLS_SSL_IS_CLIENT; break; case BCTBX_SSL_IS_SERVER: mbedtls_endpoint = MBEDTLS_SSL_IS_SERVER; break; default: return BCTBX_ERROR_INVALID_INPUT_DATA; } mbedtls_ssl_conf_endpoint(ssl_config->ssl_config, mbedtls_endpoint); return 0; } int32_t bctbx_ssl_config_set_transport (bctbx_ssl_config_t *ssl_config, int transport) { int mbedtls_transport; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* remap input arguments */ switch (transport) { case BCTBX_SSL_TRANSPORT_STREAM: mbedtls_transport = MBEDTLS_SSL_TRANSPORT_STREAM; break; case BCTBX_SSL_TRANSPORT_DATAGRAM: mbedtls_transport = MBEDTLS_SSL_TRANSPORT_DATAGRAM; break; default: return BCTBX_ERROR_INVALID_INPUT_DATA; } mbedtls_ssl_conf_transport(ssl_config->ssl_config, mbedtls_transport); return 0; } int32_t bctbx_ssl_config_set_ciphersuites(bctbx_ssl_config_t *ssl_config, const int *ciphersuites) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* remap input arguments */ if (ciphersuites == NULL) { return BCTBX_ERROR_INVALID_INPUT_DATA; } mbedtls_ssl_conf_ciphersuites(ssl_config->ssl_config, ciphersuites); return 0; } void *bctbx_ssl_config_get_private_config(bctbx_ssl_config_t *ssl_config) { return (void *)ssl_config->ssl_config; } int32_t bctbx_ssl_config_set_authmode(bctbx_ssl_config_t *ssl_config, int authmode) { int mbedtls_authmode; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* remap input arguments */ switch (authmode) { case BCTBX_SSL_VERIFY_NONE: mbedtls_authmode = MBEDTLS_SSL_VERIFY_NONE; break; case BCTBX_SSL_VERIFY_OPTIONAL: mbedtls_authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break; case BCTBX_SSL_VERIFY_REQUIRED: mbedtls_authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break; default: return BCTBX_ERROR_INVALID_SSL_AUTHMODE; break; } mbedtls_ssl_conf_authmode(ssl_config->ssl_config, mbedtls_authmode); return 0; } int32_t bctbx_ssl_config_set_rng(bctbx_ssl_config_t *ssl_config, int(*rng_function)(void *, unsigned char *, size_t), void *rng_context) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } mbedtls_ssl_conf_rng(ssl_config->ssl_config, rng_function, rng_context); return 0; } int32_t bctbx_ssl_config_set_callback_verify(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_x509_certificate_t *, int, uint32_t *), void *callback_data) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } mbedtls_ssl_conf_verify(ssl_config->ssl_config, (int(*)(void *, mbedtls_x509_crt*, int, uint32_t *))callback_function, callback_data); return 0; } int32_t bctbx_ssl_config_set_callback_cli_cert(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_ssl_context_t *, const bctbx_list_t *), void *callback_data) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } ssl_config->callback_cli_cert_function = callback_function; ssl_config->callback_cli_cert_data = callback_data; return 0; } int32_t bctbx_ssl_config_set_ca_chain(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *ca_chain) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* ca_crl (arg 3) is always set to null, add the functionnality if needed */ mbedtls_ssl_conf_ca_chain(ssl_config->ssl_config, (mbedtls_x509_crt *)ca_chain, NULL); return 0; } int32_t bctbx_ssl_config_set_own_cert(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } return mbedtls_ssl_conf_own_cert(ssl_config->ssl_config, (mbedtls_x509_crt *)cert, (mbedtls_pk_context *)key); } /** DTLS SRTP functions **/ #ifdef HAVE_DTLS_SRTP /* key derivation code */ /* This callback is executed during the DTLS handshake, extract the master secret and randoms needed to generate the DTLS-SRTP keys * The generation itself is performed after the handshake */ static int bctbx_ssl_dtls_srtp_key_derivation(void *key_ctx, const unsigned char *ms, const unsigned char *kb, size_t maclen, size_t keylen, size_t ivlen, // these params are useless for our purpose const unsigned char client_random[32], const unsigned char server_random[32], mbedtls_tls_prf_types tls_prf_type ) { bctbx_dtls_srtp_keys_t *keys = (bctbx_dtls_srtp_keys_t *)key_ctx; memcpy(keys->master_secret, ms, sizeof(keys->master_secret)); // copy the master secret memcpy(keys->randoms, client_random, 32); // the client and server random memcpy(keys->randoms + 32, server_random, 32); keys->tls_prf_type = tls_prf_type; // the prf id return(0); } int32_t bctbx_ssl_get_dtls_srtp_key_material(bctbx_ssl_config_t *ssl_config, uint8_t *output, size_t *output_length) { int ret = 0; if (ssl_config==NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } //ret = mbedtls_ssl_get_dtls_srtp_key_material(&(ssl_ctx->ssl_ctx), (unsigned char *)output, *output_length, output_length); ret = mbedtls_ssl_tls_prf( ssl_config->dtls_srtp_keys.tls_prf_type, ssl_config->dtls_srtp_keys.master_secret, sizeof( ssl_config->dtls_srtp_keys.master_secret ), "EXTRACTOR-dtls_srtp", ssl_config->dtls_srtp_keys.randoms, sizeof( ssl_config->dtls_srtp_keys.randoms ), output, *output_length ); /* remap the output error code */ if (ret == MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } return 0; } int32_t bctbx_ssl_config_set_dtls_srtp_protection_profiles(bctbx_ssl_config_t *ssl_config, const bctbx_dtls_srtp_profile_t *profiles, size_t profiles_number) { size_t i; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* convert the profiles array into a mbedtls profiles array */ for (i=0; idtls_srtp_mbedtls_profiles[i] = bctbx_srtp_profile_bctoolbox2mbedtls(profiles[i]); } for (;i<=MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH; i++) { /* make sure to have a MBEDTLS_TLS_SRTP_UNSET terminated list and harmless values in the rest of the array */ ssl_config->dtls_srtp_mbedtls_profiles[i] = MBEDTLS_TLS_SRTP_UNSET; } /* set the callback to compute the key material */ mbedtls_ssl_conf_export_keys_ext_cb(ssl_config->ssl_config, bctbx_ssl_dtls_srtp_key_derivation, &(ssl_config->dtls_srtp_keys)); return mbedtls_ssl_conf_dtls_srtp_protection_profiles(ssl_config->ssl_config, ssl_config->dtls_srtp_mbedtls_profiles); // no profile number, list is UNSET terminated } #else /* HAVE_DTLS_SRTP */ int32_t bctbx_ssl_get_dtls_srtp_key_material(bctbx_ssl_config_t *ssl_ctx, uint8_t *output, size_t *output_length) { *output_length = 0; return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } int32_t bctbx_ssl_config_set_dtls_srtp_protection_profiles(bctbx_ssl_config_t *ssl_config, const bctbx_dtls_srtp_profile_t *profiles, size_t profiles_number) { return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } #endif /* HAVE_DTLS_SRTP */ /** DTLS SRTP functions **/ int32_t bctbx_ssl_context_setup(bctbx_ssl_context_t *ssl_ctx, bctbx_ssl_config_t *ssl_config) { int ret; /* Check validity of context and config */ if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } if (ssl_ctx == NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } /* apply all valids settings to the ssl_context */ if (ssl_config->callback_cli_cert_function != NULL) { ssl_ctx->callback_cli_cert_function = ssl_config->callback_cli_cert_function; ssl_ctx->callback_cli_cert_data = ssl_config->callback_cli_cert_data; } #ifdef HAVE_DTLS_SRTP /* We do not use DTLS SRTP cookie, so we must set to NULL the callbacks. Cookies are used to prevent DoS attack but our server is on only when during a brief period so we do not need this */ mbedtls_ssl_conf_dtls_cookies(ssl_config->ssl_config, NULL, NULL, NULL); #endif /* HAVE_DTLS_SRTP */ ret = mbedtls_ssl_setup(&(ssl_ctx->ssl_ctx), ssl_config->ssl_config); if (ret != 0) return ret; /* for DTLS handshake, we must set a timer callback */ mbedtls_ssl_set_timer_cb(&(ssl_ctx->ssl_ctx), &(ssl_ctx->timer), mbedtls_timing_set_delay, mbedtls_timing_get_delay); return ret; } /*****************************************************************************/ /***** Hashing *****/ /*****************************************************************************/ /* * @brief HMAC-SHA512 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 64 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha512(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[64]; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), key, keyLength, input, inputLength, hmacOutput); /* check output length, can't be>64 */ if (hmacLength>64) { memcpy(output, hmacOutput, 64); } else { memcpy(output, hmacOutput, hmacLength); } } /* * @brief HMAC-SHA384 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha384(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[48]; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), key, keyLength, input, inputLength, hmacOutput); /* check output length, can't be>48 */ if (hmacLength>48) { memcpy(output, hmacOutput, 48); } else { memcpy(output, hmacOutput, hmacLength); } } /* * brief HMAC-SHA256 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha256(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[32]; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, keyLength, input, inputLength, hmacOutput); /* check output length, can't be>32 */ if (hmacLength>32) { memcpy(output, hmacOutput, 32); } else { memcpy(output, hmacOutput, hmacLength); } } /* * @brief SHA512 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 64 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha512(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[64]; mbedtls_sha512(input, inputLength, hashOutput, 0); /* check output length, can't be>64 */ if (hashLength>64) { memcpy(output, hashOutput, 64); } else { memcpy(output, hashOutput, hashLength); } } /* * @brief SHA384 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha384(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[64]; mbedtls_sha512(input, inputLength, hashOutput, 1); /* last param to one to select SHA384 and not SHA512 */ /* check output length, can't be>48 */ if (hashLength>48) { memcpy(output, hashOutput, 48); } else { memcpy(output, hashOutput, hashLength); } } /* * @brief SHA256 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha256(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[32]; mbedtls_sha256(input, inputLength, hashOutput, 0); /* last param to zero to select SHA256 and not SHA224 */ /* check output length, can't be>32 */ if (hashLength>32) { memcpy(output, hashOutput, 32); } else { memcpy(output, hashOutput, hashLength); } } /* * @brief HMAC-SHA1 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 20 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha1(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[20]; mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key, keyLength, input, inputLength, hmacOutput); /* check output length, can't be>20 */ if (hmacLength>20) { memcpy(output, hmacOutput, 20); } else { memcpy(output, hmacOutput, hmacLength); } } /** * @brief MD5 wrapper * output = md5(input) * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[out] output Output data buffer. * */ void bctbx_md5(const uint8_t *input, size_t inputLength, uint8_t output[16]) { mbedtls_md5(input, inputLength, output); } /*****************************************************************************/ /***** Encryption/Decryption *****/ /*****************************************************************************/ /***** GCM *****/ /** * @Brief AES-GCM encrypt and tag buffer * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] plainText Buffer to be encrypted * @param[in] plainTextLength Length in bytes of buffer to be encrypted * @param[in] authenticatedData Buffer holding additional data to be used in tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[out] tag Buffer holding the generated tag * @param[in] tagLength Requested length for the generated tag * @param[out] output Buffer holding the output, shall be at least the length of plainText buffer */ int32_t bctbx_aes_gcm_encrypt_and_tag(const uint8_t *key, size_t keyLength, const uint8_t *plainText, size_t plainTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t *tag, size_t tagLength, uint8_t *output) { mbedtls_gcm_context gcmContext; int ret; mbedtls_gcm_init(&gcmContext); ret = mbedtls_gcm_setkey(&gcmContext, MBEDTLS_CIPHER_ID_AES, key, (unsigned int)keyLength*8); if (ret != 0) return ret; ret = mbedtls_gcm_crypt_and_tag(&gcmContext, MBEDTLS_GCM_ENCRYPT, plainTextLength, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength, plainText, output, tagLength, tag); mbedtls_gcm_free(&gcmContext); return ret; } /** * @Brief AES-GCM decrypt, compute authentication tag and compare it to the one provided * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] cipherText Buffer to be decrypted * @param[in] cipherTextLength Length in bytes of buffer to be decrypted * @param[in] authenticatedData Buffer holding additional data to be used in auth tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] tag Buffer holding the authentication tag * @param[in] tagLength Length in bytes for the authentication tag * @param[out] output Buffer holding the output, shall be at least the length of cipherText buffer * * @return 0 on succes, BCTBX_ERROR_AUTHENTICATION_FAILED if tag doesn't match or mbedtls error code */ int32_t bctbx_aes_gcm_decrypt_and_auth(const uint8_t *key, size_t keyLength, const uint8_t *cipherText, size_t cipherTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, const uint8_t *tag, size_t tagLength, uint8_t *output) { mbedtls_gcm_context gcmContext; int ret; mbedtls_gcm_init(&gcmContext); ret = mbedtls_gcm_setkey(&gcmContext, MBEDTLS_CIPHER_ID_AES, key, (unsigned int)keyLength*8); if (ret != 0) return ret; ret = mbedtls_gcm_auth_decrypt(&gcmContext, cipherTextLength, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength, tag, tagLength, cipherText, output); mbedtls_gcm_free(&gcmContext); if (ret == MBEDTLS_ERR_GCM_AUTH_FAILED) { return BCTBX_ERROR_AUTHENTICATION_FAILED; } return ret; } /** * @Brief create and initialise an AES-GCM encryption context * * @param[in] key encryption key * @param[in] keyLength key buffer length, in bytes, must be 16,24 or 32 * @param[in] authenticatedData Buffer holding additional data to be used in tag computation (can be NULL) * @param[in] authenticatedDataLength additional data length in bytes (can be 0) * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] mode Operation mode : BCTBX_GCM_ENCRYPT or BCTBX_GCM_DECRYPT * * @return 0 on success, crypto library error code otherwise */ bctbx_aes_gcm_context_t *bctbx_aes_gcm_context_new(const uint8_t *key, size_t keyLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t mode) { int ret = 0; int mbedtls_mode; mbedtls_gcm_context *ctx = NULL; if (mode == BCTBX_GCM_ENCRYPT) { mbedtls_mode = MBEDTLS_GCM_ENCRYPT; } else if ( mode == BCTBX_GCM_DECRYPT) { mbedtls_mode = MBEDTLS_GCM_DECRYPT; } else { return NULL; } ctx = bctbx_malloc0(sizeof(mbedtls_gcm_context)); mbedtls_gcm_init(ctx); ret = mbedtls_gcm_setkey(ctx, MBEDTLS_CIPHER_ID_AES, key, (unsigned int)keyLength*8); if (ret != 0) { bctbx_free(ctx); return NULL; } ret = mbedtls_gcm_starts(ctx, mbedtls_mode, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength); if (ret != 0) { bctbx_free(ctx); return NULL; } return (bctbx_aes_gcm_context_t *)ctx; } /** * @Brief AES-GCM encrypt or decrypt a chunk of data * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[in] input buffer holding the input data * @param[in] inputLength lenght of the input data * @param[out] output buffer to store the output data (same length as input one) * * @return 0 on success, crypto library error code otherwise */ int32_t bctbx_aes_gcm_process_chunk(bctbx_aes_gcm_context_t *context, const uint8_t *input, size_t inputLength, uint8_t *output) { return mbedtls_gcm_update((mbedtls_gcm_context *)context, inputLength, input, output); } /** * @Brief Conclude a AES-GCM encryption stream, generate tag if requested, free resources * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[out] tag a buffer to hold the authentication tag. Can be NULL if tagLength is 0 * @param[in] tagLength length of reqested authentication tag, max 16 * * @return 0 on success, crypto library error code otherwise */ int32_t bctbx_aes_gcm_finish(bctbx_aes_gcm_context_t *context, uint8_t *tag, size_t tagLength) { int ret; ret = mbedtls_gcm_finish((mbedtls_gcm_context *)context, tag, tagLength); mbedtls_gcm_free((mbedtls_gcm_context *)context); bctbx_free(context); return ret; } /* * @brief Wrapper for AES-128 in CFB128 mode encryption * Both key and IV must be 16 bytes long, IV is not updated * * @param[in] key encryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes128CfbEncrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; /* is not used by us but needed and updated by mbedtls */ mbedtls_aes_context context; memset (&context, 0, sizeof(mbedtls_aes_context)); /* make a local copy of IV which is modified by the mbedtls AES-CFB function */ memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); /* initialise the aes context and key */ mbedtls_aes_setkey_enc(&context, key, 128); /* encrypt */ mbedtls_aes_crypt_cfb128 (&context, MBEDTLS_AES_ENCRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-128 in CFB128 mode decryption * Both key and IV must be 16 bytes long, IV is not updated * * @param[in] key decryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes128CfbDecrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; /* is not used by us but needed and updated by mbedtls */ mbedtls_aes_context context; memset (&context, 0, sizeof(mbedtls_aes_context)); /* make a local copy of IV which is modified by the mbedtls AES-CFB function */ memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); /* initialise the aes context and key - use the aes_setkey_enc function as requested by the documentation of aes_crypt_cfb128 function */ mbedtls_aes_setkey_enc(&context, key, 128); /* encrypt */ mbedtls_aes_crypt_cfb128 (&context, MBEDTLS_AES_DECRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-256 in CFB128 mode encryption * The key must be 32 bytes long and the IV must be 16 bytes long, IV is not updated * * @param[in] key encryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes256CfbEncrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; mbedtls_aes_context context; memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); memset (&context, 0, sizeof(mbedtls_aes_context)); mbedtls_aes_setkey_enc(&context, key, 256); /* encrypt */ mbedtls_aes_crypt_cfb128 (&context, MBEDTLS_AES_ENCRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-256 in CFB128 mode decryption * The key must be 32 bytes long and the IV must be 16 bytes long, IV is not updated * * @param[in] key decryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes256CfbDecrypt(const uint8_t *key, const uint8_t *IV, const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; mbedtls_aes_context context; memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); memset (&context, 0, sizeof(mbedtls_aes_context)); mbedtls_aes_setkey_enc(&context, key, 256); /* decrypt */ mbedtls_aes_crypt_cfb128 (&context, MBEDTLS_AES_DECRYPT, inputLength, &iv_offset, IVbuffer, input, output); } bctoolbox-5.2.0/src/crypto/mbedtls.cc000066400000000000000000000537701434566643100176110ustar00rootroot00000000000000/* * Copyright (c) 2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #if MBEDTLS_VERSION_NUMBER >= 0x020B0000 // v2.11.0 #include // HKDF implemented in version 2.11.0 of mbedtls #endif #include "bctoolbox/crypto.hh" #include "bctoolbox/crypto.h" #include "bctoolbox/exception.hh" #include namespace bctoolbox { /*****************************************************************************/ /*** Random Number Generation ***/ /*****************************************************************************/ /** * @brief Wrapper around mbedtls implementation **/ struct RNG::Impl { mbedtls_entropy_context entropy; /**< entropy context - store it to be able to free it */ mbedtls_ctr_drbg_context ctr_drbg; /**< rng context */ /** * Implementation constructor * Initialise the entropy and RNG context */ Impl() { mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) != 0) { throw BCTBX_EXCEPTION << "RNG failure at creation: entropy source failure"; } } ~Impl() { mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); } }; /** * Constructor * Just instanciate an implementation */ RNG::RNG() :pImpl(std::unique_ptr(new RNG::Impl())) {}; /** * Destructor * Implementation is stored in a unique_ptr, nothing to do **/ RNG::~RNG()=default; // Instanciate the class RNG context std::unique_ptr RNG::pImplClass = std::unique_ptr(new RNG::Impl()); void RNG::randomize(uint8_t *buffer, size_t size) { int ret = mbedtls_ctr_drbg_random(&(pImpl->ctr_drbg), buffer, size); if ( ret != 0) { throw BCTBX_EXCEPTION << ((ret == MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG)?"RNG failure: Request too big":"RNG failure: entropy source failure"); } } std::vector RNG::randomize(const size_t size) { std::vector buffer(size); int ret = mbedtls_ctr_drbg_random(&(pImpl->ctr_drbg), buffer.data(), size); if ( ret != 0) { throw BCTBX_EXCEPTION << ((ret == MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG)?"RNG failure: Request too big":"RNG failure: entropy source failure"); } return buffer; } uint32_t RNG::randomize() { uint8_t buffer[4]; randomize(buffer, 4); return (buffer[0]<<24) | (buffer[1]<<16) | (buffer[2]<<8) | buffer[3]; } /* * class randomize functions * These use the class RNG context */ void RNG::cRandomize(uint8_t *buffer, size_t size) { int ret = mbedtls_ctr_drbg_random(&(pImplClass->ctr_drbg), buffer, size); if ( ret != 0) { throw BCTBX_EXCEPTION << ((ret == MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG)?"RNG failure: Request too big":"RNG failure: entropy source failure"); } } uint32_t RNG::cRandomize() { uint8_t buffer[4]; cRandomize(buffer, 4); return (buffer[0]<<24) | (buffer[1]<<16) | (buffer[2]<<8) | buffer[3]; } /*****************************************************************************/ /*** Hash related function ***/ /*****************************************************************************/ /* HMAC templates */ /* HMAC must use a specialized template */ template std::vector HMAC(const std::vector &key, const std::vector &input) { /* if this template is instanciated the static_assert will fail but will give us an error message */ static_assert(sizeof(hashAlgo) != sizeof(hashAlgo), "You must specialize HMAC function template"); return std::vector(0); } /* HMAC specialized template for SHA1 */ template <> std::vector HMAC(const std::vector &key, const std::vector &input) { std::vector hmacOutput(SHA1::ssize()); mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key.data(), key.size(), input.data(), input.size(), hmacOutput.data()); return hmacOutput; } /* HMAC specialized template for SHA256 */ template <> std::vector HMAC(const std::vector &key, const std::vector &input) { std::vector hmacOutput(SHA256::ssize()); mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key.data(), key.size(), input.data(), input.size(), hmacOutput.data()); return hmacOutput; } /* HMAC specialized template for SHA384 */ template <> std::vector HMAC(const std::vector &key, const std::vector &input) { std::vector hmacOutput(SHA384::ssize()); mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), key.data(), key.size(), input.data(), input.size(), hmacOutput.data()); return hmacOutput; } /* HMAC specialized template for SHA512 */ template <> std::vector HMAC(const std::vector &key, const std::vector &input) { std::vector hmacOutput(SHA512::ssize()); mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), key.data(), key.size(), input.data(), input.size(), hmacOutput.data()); return hmacOutput; } /* HKDF templates */ /* HKDF must use a specialized template */ template std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t okmSize) { /* if this template is instanciated the static_assert will fail but will give us an error message */ static_assert(sizeof(hashAlgo) != sizeof(hashAlgo), "You must specialize HKDF function template"); return std::vector(0); } template std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t okmSize) { /* if this template is instanciated the static_assert will fail but will give us an error message */ static_assert(sizeof(hashAlgo) != sizeof(hashAlgo), "You must specialize HKDF function template"); return std::vector(0); } #if MBEDTLS_VERSION_NUMBER >= 0x020B0000 // v2.11.0 - HKDF provided by mbedtls /* HKDF specialized template for SHA256 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), salt.data(), salt.size(), ikm.data(), ikm.size(), info.data(), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA256 error"; } return okm; }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), salt.data(), salt.size(), ikm.data(), ikm.size(), reinterpret_cast(info.data()), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA256 error"; } return okm; }; /* HKDF specialized template for SHA384 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), salt.data(), salt.size(), ikm.data(), ikm.size(), info.data(), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA384 error"; } return okm; }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), salt.data(), salt.size(), ikm.data(), ikm.size(), reinterpret_cast(info.data()), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA384 error"; } return okm; }; /* HKDF specialized template for SHA512 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), salt.data(), salt.size(), ikm.data(), ikm.size(), info.data(), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA512 error"; } return okm; }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { std::vector okm(outputSize); if (mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), salt.data(), salt.size(), ikm.data(), ikm.size(), reinterpret_cast(info.data()), info.size(), okm.data(), outputSize) != 0) { throw BCTBX_EXCEPTION<<"HKDF-SHA512 error"; } return okm; }; #else // MBEDTLS_VERSION_NUMBER >= 0x020B0000 - HKDF not provided by mbedtls /* generic implementation, of HKDF RFC-5869 - use this one if mbedtls does not provide it */ template std::vector HMAC_KDF(const std::vector &salt, const std::vector &ikm, const infoType &info, size_t outputSize) { // extraction auto prk = HMAC(salt, ikm); // expansion round 0 std::vector T(info.cbegin(), info.cend()); T.push_back(0x01); auto output = HMAC(prk, T); output.reserve(outputSize); // successives expansion rounds size_t index = std::min(outputSize, hashAlgo::ssize()); for(uint8_t i=0x02; index < outputSize; i++) { T.assign(output.cbegin()+(i-2)*hashAlgo::ssize(), output.cbegin()+(i-1)*hashAlgo::ssize()); T.insert(T.end(), info.cbegin(), info.cend()); T.push_back(i); auto round = HMAC(prk, T); output.insert(output.end(), round.cbegin(), round.cend()); index += hashAlgo::ssize(); } bctbx_clean(prk.data(), prk.size()); bctbx_clean(T.data(), T.size()); output.resize(outputSize); // dump what is not needed (each round compute hashAlgo::ssize() bytes of data, we may need only a fraction of the last round) return output; } /* HKDF specialized template for SHA256 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { return HMAC_KDF>(salt, ikm, info, outputSize); }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { return HMAC_KDF(salt, ikm, info, outputSize); }; /* HKDF specialized template for SHA384 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { return HMAC_KDF>(salt, ikm, info, outputSize); }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { return HMAC_KDF(salt, ikm, info, outputSize); }; /* HKDF specialized template for SHA512 */ template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::vector &info, size_t outputSize) { return HMAC_KDF>(salt, ikm, info, outputSize); }; template <> std::vector HKDF(const std::vector &salt, const std::vector &ikm, const std::string &info, size_t outputSize) { return HMAC_KDF(salt, ikm, info, outputSize); }; #endif // MBEDTLS_VERSION_NUMBER >= 0x020B0000 /*****************************************************************************/ /*** Authenticated Encryption ***/ /*****************************************************************************/ /* AEAD template must be specialized */ template std::vector AEADEncrypt(const std::vector &key, const std::vector IV, const std::vector &plain, const std::vector &AD, std::vector &tag) { /* if this template is instanciated the static_assert will fail but will give us an error message with faulty type */ static_assert(sizeof(AEADAlgo) != sizeof(AEADAlgo), "You must specialize AEADEncrypt function template"); return std::vector(0); } template bool AEADDecrypt(const std::vector &key, const std::vector &IV, const std::vector &cipher, const std::vector &AD, const std::vector &tag, std::vector &plain) { /* if this template is instanciated the static_assert will fail but will give us an error message with faulty type */ static_assert(sizeof(AEADAlgo) != sizeof(AEADAlgo), "You must specialize AEADEncrypt function template"); return false; } /* declare AEAD template specialisations : AES256-GCM with 128 bits auth tag*/ template <> std::vector AEADEncrypt(const std::vector &key, const std::vector IV, const std::vector &plain, const std::vector &AD, std::vector &tag) { // check key size (could have use array but Windows won't compile templates with constexpr functions result as parameter) if (key.size() != AES256GCM128::keySize()) { throw BCTBX_EXCEPTION<<"AEADEncrypt: Bad input parameter, key is expected to be "< cipher(plain.size()); // cipher size is the same than plain ret = mbedtls_gcm_crypt_and_tag(&gcmContext, MBEDTLS_GCM_ENCRYPT, plain.size(), IV.data(), IV.size(), AD.data(), AD.size(), plain.data(), cipher.data(), tag.size(), tag.data()); mbedtls_gcm_free(&gcmContext); if (ret != 0) { throw BCTBX_EXCEPTION<<"Error during AES_GCM encryption : return value "< bool AEADDecrypt(const std::vector &key, const std::vector &IV, const std::vector &cipher, const std::vector &AD, const std::vector &tag, std::vector &plain) { // check key and tag size (could have use array but Windows won't compile templates with constexpr functions result as parameter) if (key.size() != AES256GCM128::keySize()) { throw BCTBX_EXCEPTION<<"AEADDecrypt: Bad input parameter, key is expected to be "< &plaintext, const std::vector &key, std::vector &ciphertext, AesId id){ uint64_t t; size_t m = plaintext.size(); // Size of the plaintext size_t r; // Size of the padded plaintext size_t n; // Number of 64-bit blocks in the padded plaintext uint8_t input[16]; // Buffer of the AES input uint8_t B[16]; // Buffer of the AES output uint8_t *R = (uint8_t *) bctbx_malloc((m+8) * sizeof(uint8_t)); // An array of 8-bit registers /* Append padding */ r = m; memcpy(R, plaintext.data(), r); while (r%8 != 0) { R[r] = 0; r++; } n = r / 8; /* Initialise variables */ // Initialisation of A described in the RFC 5649 Section 3 : https://datatracker.ietf.org/doc/html/rfc5649#section-3 uint8_t A[8]; A[0] = 0xA6; A[1] = 0x59; A[2] = 0x59; A[3] = 0xA6; for (size_t i = 0 ; i < 4 ; i++) { A[4+i] = (m >> (3-i)*8) & 0xFF; } // Initialise AES context with the key mbedtls_aes_context context; mbedtls_aes_init(&context); switch (id) { case AesId::AES128 : mbedtls_aes_setkey_enc(&context, key.data(), 128); break; case AesId::AES192 : mbedtls_aes_setkey_enc(&context, key.data(), 192); break; case AesId::AES256 : mbedtls_aes_setkey_enc(&context, key.data(), 256); break; default : return BCTBX_ERROR_INVALID_INPUT_DATA; } /* Calculate intermediate values */ if (r == 8) { // input = concat(A, R[0]) memcpy(input, A, 8); memcpy(input+8, R, 8); // B = AES(key, input) mbedtls_aes_crypt_ecb(&context, MBEDTLS_AES_ENCRYPT, input, B); /* Output the results */ ciphertext.assign(B, B+16); mbedtls_aes_free(&context); bctbx_free(R); return 0; } for (int j = 0 ; j <= 5 ; j++) { for (size_t i = 0 ; i < n ; i++) { // input = concat(A, R[i]) memcpy(input, A, 8); memcpy(input+8, R+(i*8), 8); // B = AES(key, input) mbedtls_aes_crypt_ecb(&context, MBEDTLS_AES_ENCRYPT, input, B); // A = MSB(64, B) ^ t where t = (n*j)+i t = (n * j) + i+1; for (size_t k = 0 ; k < 8 ; k++) { A[k] = B[k] ^ ((t >> (7-k)*8) & 0xFF); } // R[i] = LSB(64, B) memcpy(R+(i*8), B+8, 8); } } /* Output the results */ ciphertext.assign(A, A+8); ciphertext.insert(ciphertext.end(), R, R+r); mbedtls_aes_free(&context); bctbx_free(R); return 0; } int AES_key_unwrap(const std::vector &ciphertext, const std::vector &key, std::vector &plaintext, AesId id){ size_t n = (ciphertext.size() - 8) / 8; // Number of 64-bit blocks of the padded plaintext size_t r = ciphertext.size() - 8; // Size of the padded plaintext size_t m = 0; // Size of the plaintext uint8_t input[16]; // Buffer of the AES input uint8_t B[16]; // Buffer of the AES output uint8_t *R = (uint8_t *) bctbx_malloc(r * sizeof(uint8_t)); // An array of 8-bit registers /* Initialise variables */ uint8_t A[8]; memcpy(A, ciphertext.data(), 8); memcpy(R, ciphertext.data()+8, r); // Initialise AES context with the key mbedtls_aes_context context; mbedtls_aes_init(&context); switch (id) { case AesId::AES128 : mbedtls_aes_setkey_dec(&context, key.data(), 128); break; case AesId::AES192 : mbedtls_aes_setkey_dec(&context, key.data(), 192); break; case AesId::AES256 : mbedtls_aes_setkey_dec(&context, key.data(), 256); break; default : mbedtls_aes_free(&context); bctbx_free(R); return BCTBX_ERROR_INVALID_INPUT_DATA; } /* Compute intermediate values */ if (n == 1) { // input = ciphertext memcpy(input, ciphertext.data(), 16); // B = AES-1(K, input) mbedtls_aes_crypt_ecb(&context, MBEDTLS_AES_DECRYPT, input, B); // A = MSB(64, B) memcpy(A, B, 8); // R[i] = LSB(64, B) memcpy(R, B+8, 8); } else { for (int j = 5 ; j >= 0 ; j--) { for (size_t i = n ; i > 0 ; i--) { // input = concat(A ^ t, R[i]) where t = n*j+i uint64_t t = (n * j) + i; for (size_t k = 0 ; k < 8 ; k++) { input[k] = A[k] ^ ((t >> (7-k)*8) & 0xFF); input[k+8] = R[(i-1)*8+k]; } // B = AES-1(K, input) mbedtls_aes_crypt_ecb(&context, MBEDTLS_AES_DECRYPT, input, B); // A = MSB(64, B) memcpy(A, B, 8); // R[i] = LSB(64, B) memcpy(R+((i-1)*8), B+8, 8); } } } /* Output the results */ mbedtls_aes_free(&context); // AIV verification described in the RFC 5649 Section 3 : https://datatracker.ietf.org/doc/html/rfc5649#section-3 if (!(A[0] == 0xA6 && A[1] == 0x59 && A[2] == 0x59 && A[3] == 0xA6)){ bctbx_free(R); return BCTBX_ERROR_UNSPECIFIED_ERROR; } // m = concat(A[4], A[5], A[6], A[7]) for (size_t i = 0 ; i < 4 ; i++) { m |= A[4+i] << ((3-i)*8); } if ((m <= 8*(n-1)) || (8*n <= m)) { bctbx_free(R); return BCTBX_ERROR_UNSPECIFIED_ERROR; } // Remove padding & Return plaintext plaintext.assign(R, R+m); bctbx_free(R); return 0; } } // namespace bctoolbox /*** Random Number Generation: C API ***/ struct bctbx_rng_context_struct { std::unique_ptr m_rng; // encapsulate the RNG in a unique_ptr }; bctbx_rng_context_t *bctbx_rng_context_new(void) { bctbx_rng_context_t *context = new bctbx_rng_context_struct(); context->m_rng = std::unique_ptr(new bctoolbox::RNG()); return context; } int32_t bctbx_rng_get(bctbx_rng_context_t *context, unsigned char*output, size_t output_length) { context->m_rng->randomize(output, output_length); return 0; // always return 0, in case of problem an exception is raised by randomize } void bctbx_rng_context_free(bctbx_rng_context_t *context) { context->m_rng=nullptr; // destroy the RNG delete(context); } bctoolbox-5.2.0/src/crypto/polarssl.c000066400000000000000000001615701434566643100176510ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "utils.h" #include #include #include #include #include "polarssl/base64.h" #include #include #include #include #include #include #include #include #include "bctoolbox/logging.h" #ifdef _WIN32 #define snprintf _snprintf #endif /*** Cleaning ***/ /** * @brief force a buffer value to zero in a way that shall prevent the compiler from optimizing it out * * @param[in/out] buffer the buffer to be cleared * @param[in] size buffer size */ void bctbx_clean(void *buffer, size_t size) { volatile uint8_t *p = buffer; while(size--) *p++ = 0; } static int bctbx_ssl_sendrecv_callback_return_remap(int32_t ret_code) { switch (ret_code) { case BCTBX_ERROR_NET_WANT_READ: return POLARSSL_ERR_NET_WANT_READ; case BCTBX_ERROR_NET_WANT_WRITE: return POLARSSL_ERR_NET_WANT_WRITE; case BCTBX_ERROR_NET_CONN_RESET: return POLARSSL_ERR_NET_CONN_RESET; default: return (int)ret_code; } } bctbx_type_implementation_t bctbx_ssl_get_implementation_type(void) { return BCTBX_POLARSSL; } void bctbx_strerror(int32_t error_code, char *buffer, size_t buffer_length) { if (error_code>0) { snprintf(buffer, buffer_length, "%s", "Invalid Error code"); return ; } /* polarssl error code are all negatived and bas smaller than 0x0000F000 */ /* bctoolbox defined error codes are all in format -0x7XXXXXXX */ if (-error_code<0x00010000) { /* it's a polarssl error code */ error_strerror(error_code, buffer, buffer_length); return; } snprintf(buffer, buffer_length, "%s [-0x%x]", "bctoolbox defined error code", -error_code); return; } int32_t bctbx_base64_encode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length) { int ret = base64_encode(output, output_length, input, input_length); if (ret == POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } return ret; } int32_t bctbx_base64_decode(unsigned char *output, size_t *output_length, const unsigned char *input, size_t input_length) { int ret = base64_decode(output, output_length, input, input_length); if (ret == POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } if (ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER) { return BCTBX_ERROR_INVALID_BASE64_INPUT; } return ret; } /*** Random Number Generation ***/ struct bctbx_rng_context_struct { entropy_context entropy; ctr_drbg_context ctr_drbg; }; bctbx_rng_context_t *bctbx_rng_context_new(void) { bctbx_rng_context_t *ctx = bctbx_malloc0(sizeof(bctbx_rng_context_t)); entropy_init(&(ctx->entropy)); ctr_drbg_init(&(ctx->ctr_drbg), entropy_func, &(ctx->entropy), NULL, 0); return ctx; } int32_t bctbx_rng_get(bctbx_rng_context_t *context, unsigned char*output, size_t output_length) { return ctr_drbg_random(&(context->ctr_drbg), output, output_length); } void bctbx_rng_context_free(bctbx_rng_context_t *context) { /* ctr_drg_free function is available from polarssl1.3.8 but we want to support previous versions */ #ifdef HAVE_CTR_DRGB_FREE ctr_drbg_free(&(context->ctr_drbg)); #endif entropy_free(&(context->entropy)); bctbx_free(context); } /*** signing key ***/ bctbx_signing_key_t *bctbx_signing_key_new(void) { pk_context *key = bctbx_malloc0(sizeof(pk_context)); pk_init(key); return (bctbx_signing_key_t *)key; } void bctbx_signing_key_free(bctbx_signing_key_t *key) { pk_free((pk_context *)key); bctbx_free(key); } char *bctbx_signing_key_get_pem(bctbx_signing_key_t *key) { char *pem_key; if (key == NULL) return NULL; pem_key = (char *)bctbx_malloc0(4096); pk_write_key_pem( (pk_context *)key, (unsigned char *)pem_key, 4096); return pem_key; } int32_t bctbx_signing_key_parse(bctbx_signing_key_t *key, const char *buffer, size_t buffer_length, const unsigned char *password, size_t password_length) { int err; err=pk_parse_key((pk_context *)key, (const unsigned char *)buffer, buffer_length+1, password, password_length); if(err==0 && !pk_can_do((pk_context *)key, POLARSSL_PK_RSA)) { err=POLARSSL_ERR_PK_TYPE_MISMATCH; } if (err<0) { char tmp[128]; error_strerror(err,tmp,sizeof(tmp)); bctbx_error("cannot parse public key because [%s]",tmp); return BCTBX_ERROR_UNABLE_TO_PARSE_KEY; } return 0; } int32_t bctbx_signing_key_parse_file(bctbx_signing_key_t *key, const char *path, const char *password) { int err; err=pk_parse_keyfile((pk_context *)key, path, password); if(err==0 && !pk_can_do((pk_context *)key,POLARSSL_PK_RSA)) { err=POLARSSL_ERR_PK_TYPE_MISMATCH; } if (err<0) { char tmp[128]; error_strerror(err,tmp,sizeof(tmp)); bctbx_error("cannot parse public key because [%s]",tmp); return BCTBX_ERROR_UNABLE_TO_PARSE_KEY; } return 0; } /*** Certificate ***/ char *bctbx_x509_certificates_chain_get_pem(const bctbx_x509_certificate_t *cert) { char *pem_certificate = NULL; size_t olen=0; pem_certificate = (char *)bctbx_malloc0(4096); pem_write_buffer("-----BEGIN CERTIFICATE-----\n", "-----END CERTIFICATE-----\n", ((x509_crt *)cert)->raw.p, ((x509_crt *)cert)->raw.len, (unsigned char*)pem_certificate, 4096, &olen ); return pem_certificate; } bctbx_x509_certificate_t *bctbx_x509_certificate_new(void) { x509_crt *cert = bctbx_malloc0(sizeof(x509_crt)); x509_crt_init(cert); return (bctbx_x509_certificate_t *)cert; } void bctbx_x509_certificate_free(bctbx_x509_certificate_t *cert) { x509_crt_free((x509_crt *)cert); bctbx_free(cert); } int32_t bctbx_x509_certificate_get_info_string(char *buf, size_t size, const char *prefix, const bctbx_x509_certificate_t *cert) { return x509_crt_info(buf, size, prefix, (x509_crt *)cert); } int32_t bctbx_x509_certificate_parse_file(bctbx_x509_certificate_t *cert, const char *path) { return x509_crt_parse_file((x509_crt *)cert, path); } int32_t bctbx_x509_certificate_parse_path(bctbx_x509_certificate_t *cert, const char *path) { return x509_crt_parse_path((x509_crt *)cert, path); } int32_t bctbx_x509_certificate_parse(bctbx_x509_certificate_t *cert, const char *buffer, size_t buffer_length) { return x509_crt_parse((x509_crt *)cert, (const unsigned char *)buffer, buffer_length+1); } int32_t bctbx_x509_certificate_get_der_length(bctbx_x509_certificate_t *cert) { if (cert!=NULL) { return ((x509_crt *)cert)->raw.len; } return 0; } int32_t bctbx_x509_certificate_get_der(bctbx_x509_certificate_t *cert, unsigned char *buffer, size_t buffer_length) { if (cert==NULL) { return BCTBX_ERROR_INVALID_CERTIFICATE; } if (((x509_crt *)cert)->raw.len>buffer_length-1) { /* check buffer size is ok, +1 for the NULL termination added at the end */ return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } memcpy(buffer, ((x509_crt *)cert)->raw.p, ((x509_crt *)cert)->raw.len); buffer[((x509_crt *)cert)->raw.len] = '\0'; /* add a null termination char */ return 0; } int32_t bctbx_x509_certificate_get_subject_dn(const bctbx_x509_certificate_t *cert, char *dn, size_t dn_length) { if (cert==NULL) { return BCTBX_ERROR_INVALID_CERTIFICATE; } return x509_dn_gets(dn, dn_length, &(((x509_crt *)cert)->subject)); } bctbx_list_t *bctbx_x509_certificate_get_subjects(const bctbx_x509_certificate_t *cert){ bctbx_error("bctbx_x509_certificate_get_subjects(): not implemented for polarssl."); return NULL; } int32_t bctbx_x509_certificate_generate_selfsigned(const char *subject, bctbx_x509_certificate_t *certificate, bctbx_signing_key_t *pkey, char * pem, size_t pem_length) { entropy_context entropy; ctr_drbg_context ctr_drbg; int ret; mpi serial; x509write_cert crt; char file_buffer[8192]; size_t file_buffer_len = 0; char formatted_subject[512]; /* subject may be a sip URL or linphone-dtls-default-identity, add CN= before it to make a valid name */ memcpy(formatted_subject, "CN=", 3); memcpy(formatted_subject+3, subject, strlen(subject)+1); /* +1 to get the \0 termination */ entropy_init( &entropy ); if( ( ret = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy, NULL, 0 ) ) != 0 ) { bctbx_error("Certificate generation can't init ctr_drbg: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* generate 3072 bits RSA public/private key */ if ( (ret = pk_init_ctx( (pk_context *)pkey, pk_info_from_type( POLARSSL_PK_RSA ) )) != 0) { bctbx_error("Certificate generation can't init pk_ctx: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( ( ret = rsa_gen_key( pk_rsa( *(pk_context *)pkey ), ctr_drbg_random, &ctr_drbg, 3072, 65537 ) ) != 0) { bctbx_error("Certificate generation can't generate rsa key: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* if there is no pem pointer, don't save the key in pem format */ if (pem!=NULL) { pk_write_key_pem((pk_context *)pkey, (unsigned char *)file_buffer, 4096); file_buffer_len = strlen(file_buffer); } /* generate the certificate */ x509write_crt_init( &crt ); x509write_crt_set_md_alg( &crt, POLARSSL_MD_SHA256 ); mpi_init( &serial ); if ( (ret = mpi_read_string( &serial, 10, "1" ) ) != 0 ) { bctbx_error("Certificate generation can't read serial mpi: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } x509write_crt_set_subject_key( &crt, (pk_context *)pkey); x509write_crt_set_issuer_key( &crt, (pk_context *)pkey); if ( (ret = x509write_crt_set_subject_name( &crt, formatted_subject) ) != 0) { bctbx_error("Certificate generation can't set subject name: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( (ret = x509write_crt_set_issuer_name( &crt, formatted_subject) ) != 0) { bctbx_error("Certificate generation can't set issuer name: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } if ( (ret = x509write_crt_set_serial( &crt, &serial ) ) != 0) { bctbx_error("Certificate generation can't set serial: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } mpi_free(&serial); if ( (ret = x509write_crt_set_validity( &crt, "20010101000000", "20300101000000" ) ) != 0) { bctbx_error("Certificate generation can't set validity: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_GENERATION_FAIL; } /* store anyway certificate in pem format in a string even if we do not have file to write as we need it to get it in a x509_crt structure */ if ( (ret = x509write_crt_pem( &crt, (unsigned char *)file_buffer+file_buffer_len, 4096, ctr_drbg_random, &ctr_drbg ) ) != 0) { bctbx_error("Certificate generation can't write crt pem: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_WRITE_PEM; } x509write_crt_free(&crt); /* ctr_drg_free function is available from polarssl1.3.8 but we want to support previous versions */ #ifdef HAVE_CTR_DRGB_FREE ctr_drbg_free(&ctr_drbg); #endif entropy_free(&entropy); /* copy the key+cert in pem format into the given buffer */ if (pem != NULL) { if (strlen(file_buffer)+1>pem_length) { bctbx_error("Certificate generation can't copy the certificate to pem buffer: too short [%ld] but need [%ld] bytes", (long)pem_length, (long)strlen(file_buffer)); return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } strncpy(pem, file_buffer, pem_length); } if ( (ret = x509_crt_parse((x509_crt *)certificate, (unsigned char *)file_buffer, strlen(file_buffer)) ) != 0) { bctbx_error("Certificate generation can't parse crt pem: -%x", -ret); return BCTBX_ERROR_CERTIFICATE_PARSE_PEM; } return 0; } int32_t bctbx_x509_certificate_get_signature_hash_function(const bctbx_x509_certificate_t *certificate, bctbx_md_type_t *hash_algorithm) { x509_crt *crt; if (certificate == NULL) return BCTBX_ERROR_INVALID_CERTIFICATE; crt = (x509_crt *)certificate; switch (crt->sig_md) { case POLARSSL_MD_SHA1: *hash_algorithm = BCTBX_MD_SHA1; break; case POLARSSL_MD_SHA224: *hash_algorithm = BCTBX_MD_SHA224; break; case POLARSSL_MD_SHA256: *hash_algorithm = BCTBX_MD_SHA256; break; case POLARSSL_MD_SHA384: *hash_algorithm = BCTBX_MD_SHA384; break; case POLARSSL_MD_SHA512: *hash_algorithm = BCTBX_MD_SHA512; break; default: *hash_algorithm = BCTBX_MD_UNDEFINED; return BCTBX_ERROR_UNSUPPORTED_HASH_FUNCTION; break; } return 0; } /* maximum length of returned buffer will be 7(SHA-512 string)+3*hash_length(64)+null char = 200 bytes */ int32_t bctbx_x509_certificate_get_fingerprint(const bctbx_x509_certificate_t *certificate, char *fingerprint, size_t fingerprint_length, bctbx_md_type_t hash_algorithm) { unsigned char buffer[64]={0}; /* buffer is max length of returned hash, which is 64 in case we use sha-512 */ size_t hash_length = 0; const char *hash_alg_string=NULL; size_t fingerprint_size = 0; x509_crt *crt; md_type_t hash_id; if (certificate == NULL) return BCTBX_ERROR_INVALID_CERTIFICATE; crt = (x509_crt *)certificate; /* if there is a specified hash algorithm, use it*/ switch (hash_algorithm) { case BCTBX_MD_SHA1: hash_id = POLARSSL_MD_SHA1; break; case BCTBX_MD_SHA224: hash_id = POLARSSL_MD_SHA224; break; case BCTBX_MD_SHA256: hash_id = POLARSSL_MD_SHA256; break; case BCTBX_MD_SHA384: hash_id = POLARSSL_MD_SHA384; break; case BCTBX_MD_SHA512: hash_id = POLARSSL_MD_SHA512; break; default: /* nothing specified, use the hash algo used in the certificate signature */ hash_id = crt->sig_md; break; } /* fingerprint is a hash of the DER formated certificate (found in crt->raw.p) using the same hash function used by certificate signature */ switch (hash_id) { case POLARSSL_MD_SHA1: sha1(crt->raw.p, crt->raw.len, buffer); hash_length = 20; hash_alg_string="SHA-1"; break; case POLARSSL_MD_SHA224: sha256(crt->raw.p, crt->raw.len, buffer, 1); /* last argument is a boolean, indicate to output sha-224 and not sha-256 */ hash_length = 28; hash_alg_string="SHA-224"; break; case POLARSSL_MD_SHA256: sha256(crt->raw.p, crt->raw.len, buffer, 0); hash_length = 32; hash_alg_string="SHA-256"; break; case POLARSSL_MD_SHA384: sha512(crt->raw.p, crt->raw.len, buffer, 1); /* last argument is a boolean, indicate to output sha-384 and not sha-512 */ hash_length = 48; hash_alg_string="SHA-384"; break; case POLARSSL_MD_SHA512: sha512(crt->raw.p, crt->raw.len, buffer, 0); hash_length = 64; hash_alg_string="SHA-512"; break; default: return BCTBX_ERROR_UNSUPPORTED_HASH_FUNCTION; break; } if (hash_length>0) { size_t i; int fingerprint_index = strlen(hash_alg_string); char prefix=' '; fingerprint_size=fingerprint_index+3*hash_length+1; /* fingerprint will be : hash_alg_string+' '+HEX : separated values: length is strlen(hash_alg_string)+3*hash_lenght + 1 for null termination */ if (fingerprint_lengthbuffer_size) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } strncpy(buffer, outputString, buffer_size); return 0; } int32_t bctbx_x509_certificate_set_flag(uint32_t *flags, uint32_t flags_to_set) { if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED) *flags |= BADCERT_EXPIRED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED) *flags |= BADCERT_REVOKED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH) *flags |= BADCERT_CN_MISMATCH; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED) *flags |= BADCERT_NOT_TRUSTED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING) *flags |= BADCERT_MISSING; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED) *flags |= BADCRL_NOT_TRUSTED; if (flags_to_set & BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED) *flags |= BADCRL_EXPIRED; return 0; } uint32_t bctbx_x509_certificate_remap_flag(uint32_t flags) { uint32_t ret = 0; if (flags & BADCERT_EXPIRED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED; if (flags & BADCERT_REVOKED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED; if (flags & BADCERT_CN_MISMATCH) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH; if (flags & BADCERT_NOT_TRUSTED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED; if (flags & BADCERT_MISSING) ret |= BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING; if (flags & BADCRL_NOT_TRUSTED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED; if (flags & BADCRL_EXPIRED) ret |= BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED; return ret; } int32_t bctbx_x509_certificate_unset_flag(uint32_t *flags, uint32_t flags_to_unset) { if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_EXPIRED) *flags &= ~BADCERT_EXPIRED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_REVOKED) *flags &= ~BADCERT_REVOKED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_CN_MISMATCH) *flags &= ~BADCERT_CN_MISMATCH; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_NOT_TRUSTED) *flags &= ~BADCERT_NOT_TRUSTED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCERT_MISSING) *flags &= ~BADCERT_MISSING; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_NOT_TRUSTED) *flags &= ~BADCRL_NOT_TRUSTED; if (flags_to_unset & BCTBX_CERTIFICATE_VERIFY_BADCRL_EXPIRED) *flags &= ~BADCRL_EXPIRED; return 0; } /*** Diffie-Hellman-Merkle ***/ /* initialise de DHM context according to requested algorithm */ bctbx_DHMContext_t *bctbx_CreateDHMContext(uint8_t DHMAlgo, uint8_t secretLength) { dhm_context *polarsslDhmContext; /* create the context */ bctbx_DHMContext_t *context = (bctbx_DHMContext_t *)malloc(sizeof(bctbx_DHMContext_t)); memset (context, 0, sizeof(bctbx_DHMContext_t)); /* create the polarssl context for DHM */ polarsslDhmContext=(dhm_context *)malloc(sizeof(dhm_context)); memset(polarsslDhmContext, 0, sizeof(dhm_context)); context->cryptoModuleData=(void *)polarsslDhmContext; /* initialise pointer to NULL to ensure safe call to free() when destroying context */ context->secret = NULL; context->self = NULL; context->key = NULL; context->peer = NULL; /* set parameters in the context */ context->algo=DHMAlgo; context->secretLength = secretLength; switch (DHMAlgo) { case BCTBX_DHM_2048: /* set P and G in the polarssl context */ if ((mpi_read_string(&(polarsslDhmContext->P), 16, POLARSSL_DHM_RFC3526_MODP_2048_P) != 0) || (mpi_read_string(&(polarsslDhmContext->G), 16, POLARSSL_DHM_RFC3526_MODP_2048_G) != 0)) { return NULL; } context->primeLength=256; polarsslDhmContext->len=256; break; case BCTBX_DHM_3072: /* set P and G in the polarssl context */ if ((mpi_read_string(&(polarsslDhmContext->P), 16, POLARSSL_DHM_RFC3526_MODP_3072_P) != 0) || (mpi_read_string(&(polarsslDhmContext->G), 16, POLARSSL_DHM_RFC3526_MODP_3072_G) != 0)) { return NULL; } context->primeLength=384; polarsslDhmContext->len=384; break; default: free(context); return NULL; break; } return context; } /* generate the random secret and compute the public value */ void bctbx_DHMCreatePublic(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { /* get the polarssl context */ dhm_context *polarsslContext = (dhm_context *)context->cryptoModuleData; /* allocate output buffer */ context->self = (uint8_t *)malloc(context->primeLength*sizeof(uint8_t)); dhm_make_public(polarsslContext, context->secretLength, context->self, context->primeLength, (int (*)(void *, unsigned char *, size_t))rngFunction, rngContext); } /* compute secret - the ->peer field of context must have been set before calling this function */ void bctbx_DHMComputeSecret(bctbx_DHMContext_t *context, int (*rngFunction)(void *, uint8_t *, size_t), void *rngContext) { size_t keyLength; uint8_t sharedSecretBuffer[384]; /* longest shared secret available in these mode */ /* import the peer public value G^Y mod P in the polar ssl context */ dhm_read_public((dhm_context *)(context->cryptoModuleData), context->peer, context->primeLength); /* compute the secret key */ keyLength = context->primeLength; /* undocumented but this value seems to be in/out, so we must set it to the expected key length */ context->key = (uint8_t *)malloc(keyLength*sizeof(uint8_t)); /* allocate and reset the key buffer */ memset(context->key,0, keyLength); dhm_calc_secret((dhm_context *)(context->cryptoModuleData), sharedSecretBuffer, &keyLength, (int (*)(void *, unsigned char *, size_t))rngFunction, rngContext); /* now copy the resulting secret in the correct place in buffer(result may actually miss some front zero bytes, real length of output is now in keyLength but we want primeLength bytes) */ memcpy(context->key+(context->primeLength-keyLength), sharedSecretBuffer, keyLength); memset(sharedSecretBuffer, 0, 384); /* purge secret from temporary buffer */ } /* clean DHM context */ void bctbx_DestroyDHMContext(bctbx_DHMContext_t *context) { if (context!= NULL) { /* key and secret must be erased from memory and not just freed */ if (context->secret != NULL) { memset(context->secret, 0, context->secretLength); free(context->secret); } free(context->self); if (context->key != NULL) { memset(context->key, 0, context->primeLength); free(context->key); } free(context->peer); dhm_free((dhm_context *)context->cryptoModuleData); free(context->cryptoModuleData); free(context); } } /*** SSL Client ***/ /** context **/ struct bctbx_ssl_context_struct { ssl_context ssl_ctx; char *cn; int(*callback_cli_cert_function)(void *, bctbx_ssl_context_t *, unsigned char *, size_t); /**< pointer to the callback called to update client certificate during handshake callback params are user_data, ssl_context, certificate distinguished name, name length */ void *callback_cli_cert_data; /**< data passed to the client cert callback */ int(*callback_send_function)(void *, const unsigned char *, size_t); /* callbacks args are: callback data, data buffer to be send, size of data buffer */ int(*callback_recv_function)(void *, unsigned char *, size_t); /* args: callback data, data buffer to be read, size of data buffer */ void *callback_sendrecv_data; /**< data passed to send/recv callbacks */ }; bctbx_ssl_context_t *bctbx_ssl_context_new(void) { bctbx_ssl_context_t *ssl_ctx = bctbx_malloc0(sizeof(bctbx_ssl_context_t)); ssl_init(&(ssl_ctx->ssl_ctx)); ssl_ctx->callback_cli_cert_function = NULL; ssl_ctx->callback_cli_cert_data = NULL; ssl_ctx->callback_send_function = NULL; ssl_ctx->callback_recv_function = NULL; ssl_ctx->callback_sendrecv_data = NULL; return ssl_ctx; } void bctbx_ssl_context_free(bctbx_ssl_context_t *ssl_ctx) { if (ssl_ctx->cn) bctbx_free(ssl_ctx->cn); ssl_free(&(ssl_ctx->ssl_ctx)); bctbx_free(ssl_ctx); } int32_t bctbx_ssl_close_notify(bctbx_ssl_context_t *ssl_ctx) { return ssl_close_notify(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_session_reset(bctbx_ssl_context_t *ssl_ctx) { return ssl_session_reset(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_write(bctbx_ssl_context_t *ssl_ctx, const unsigned char *buf, size_t buf_length) { int ret = ssl_write(&(ssl_ctx->ssl_ctx), buf, buf_length); /* remap some output code */ if (ret == POLARSSL_ERR_NET_WANT_WRITE) { ret = BCTBX_ERROR_NET_WANT_WRITE; } return ret; } int32_t bctbx_ssl_read(bctbx_ssl_context_t *ssl_ctx, unsigned char *buf, size_t buf_length) { int ret = ssl_read(&(ssl_ctx->ssl_ctx), buf, buf_length); /* remap some output code */ if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) { ret = BCTBX_ERROR_SSL_PEER_CLOSE_NOTIFY; } if (ret == POLARSSL_ERR_NET_WANT_READ) { ret = BCTBX_ERROR_NET_WANT_READ; } return ret; } int32_t bctbx_ssl_handshake(bctbx_ssl_context_t *ssl_ctx) { int ret = 0; while( ssl_ctx->ssl_ctx.state != SSL_HANDSHAKE_OVER ) { ret = ssl_handshake_step(&(ssl_ctx->ssl_ctx)); if( ret != 0 ) { break; } /* insert the callback function for client certificate request */ if (ssl_ctx->callback_cli_cert_function != NULL) { /* check we have a callback function */ /* when in state SSL_CLIENT_CERTIFICATE - which means, next call to ssl_handshake_step will send the client certificate to server - * and the client_auth flag is set - which means the server requested a client certificate - */ if (ssl_ctx->ssl_ctx.state == SSL_CLIENT_CERTIFICATE && ssl_ctx->ssl_ctx.client_auth > 0) { /* note: polarssl 1.3 is unable to retrieve certificate dn during handshake from server certificate request * so the dn params in the callback are set to NULL and 0(dn string length) */ if (ssl_ctx->callback_cli_cert_function(ssl_ctx->callback_cli_cert_data, ssl_ctx, NULL, 0)!=0) { if((ret=ssl_send_fatal_handshake_failure(&(ssl_ctx->ssl_ctx))) != 0 ) return( ret ); } } } } /* remap some output codes */ if (ret == POLARSSL_ERR_NET_WANT_READ) { ret = BCTBX_ERROR_NET_WANT_READ; } else if (ret == POLARSSL_ERR_NET_WANT_WRITE) { ret = BCTBX_ERROR_NET_WANT_WRITE; } return(ret); } int32_t bctbx_ssl_set_hs_own_cert(bctbx_ssl_context_t *ssl_ctx, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key) { return ssl_set_own_cert(&(ssl_ctx->ssl_ctx) , (x509_crt *)cert , (pk_context *)key); } int bctbx_ssl_send_callback(void *data, const unsigned char *buffer, size_t buffer_length) { int ret = 0; /* data is the ssl_context which contains the actual callback and data */ bctbx_ssl_context_t *ssl_ctx = (bctbx_ssl_context_t *)data; ret = ssl_ctx->callback_send_function(ssl_ctx->callback_sendrecv_data, buffer, buffer_length); return bctbx_ssl_sendrecv_callback_return_remap(ret); } int bctbx_ssl_recv_callback(void *data, unsigned char *buffer, size_t buffer_length) { int ret = 0; /* data is the ssl_context which contains the actual callback and data */ bctbx_ssl_context_t *ssl_ctx = (bctbx_ssl_context_t *)data; ret = ssl_ctx->callback_recv_function(ssl_ctx->callback_sendrecv_data, buffer, buffer_length); return bctbx_ssl_sendrecv_callback_return_remap(ret); } void bctbx_ssl_set_io_callbacks(bctbx_ssl_context_t *ssl_ctx, void *callback_data, int(*callback_send_function)(void *, const unsigned char *, size_t), /* callbacks args are: callback data, data buffer to be send, size of data buffer */ int(*callback_recv_function)(void *, unsigned char *, size_t)){ /* args: callback data, data buffer to be read, size of data buffer */ if (ssl_ctx==NULL) { return; } ssl_ctx->callback_send_function = callback_send_function; ssl_ctx->callback_recv_function = callback_recv_function; ssl_ctx->callback_sendrecv_data = callback_data; ssl_set_bio(&(ssl_ctx->ssl_ctx), bctbx_ssl_recv_callback, ssl_ctx, bctbx_ssl_send_callback, ssl_ctx); } const bctbx_x509_certificate_t *bctbx_ssl_get_peer_certificate(bctbx_ssl_context_t *ssl_ctx) { return (const bctbx_x509_certificate_t *)ssl_get_peer_cert(&(ssl_ctx->ssl_ctx)); } const char *bctbx_ssl_get_ciphersuite(bctbx_ssl_context_t *ssl_ctx){ return ssl_get_ciphersuite(&(ssl_ctx->ssl_ctx)); } int bctbx_ssl_get_ciphersuite_id(const char *ciphersuite){ return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } const char *bctbx_ssl_get_version(bctbx_ssl_context_t *ssl_ctx){ return ssl_get_version(&(ssl_ctx->ssl_ctx)); } int32_t bctbx_ssl_set_hostname(bctbx_ssl_context_t *ssl_ctx, const char *hostname){ if (ssl_ctx->cn) bctbx_free(ssl_ctx->cn); if (hostname) ssl_ctx->cn = bctbx_strdup(hostname); else ssl_ctx->cn = NULL; ssl_ctx->ssl_ctx.peer_cn = ssl_ctx->cn; return 0; } /** DTLS SRTP functions **/ /* used for DTLS SRTP but not implemented on polarssl, available on mbedtls only */ void bctbx_ssl_set_mtu(bctbx_ssl_context_t *ssl_ctx, uint16_t mtu) { }; #ifdef HAVE_DTLS_SRTP uint8_t bctbx_dtls_srtp_supported(void) { return 1; } static bctbx_dtls_srtp_profile_t bctbx_srtp_profile_polarssl2bctoolbox(enum DTLS_SRTP_protection_profiles polarssl_profile) { switch (polarssl_profile) { case SRTP_AES128_CM_HMAC_SHA1_80: return BCTBX_SRTP_AES128_CM_HMAC_SHA1_80; case SRTP_AES128_CM_HMAC_SHA1_32: return BCTBX_SRTP_AES128_CM_HMAC_SHA1_32; case SRTP_NULL_HMAC_SHA1_80: return BCTBX_SRTP_NULL_HMAC_SHA1_80; case SRTP_NULL_HMAC_SHA1_32: return BCTBX_SRTP_NULL_HMAC_SHA1_32; default: return BCTBX_SRTP_UNDEFINED; } } static enum DTLS_SRTP_protection_profiles bctbx_srtp_profile_bctoolbox2polarssl(bctbx_dtls_srtp_profile_t bctbx_profile) { switch (bctbx_profile) { case BCTBX_SRTP_AES128_CM_HMAC_SHA1_80: return SRTP_AES128_CM_HMAC_SHA1_80; case BCTBX_SRTP_AES128_CM_HMAC_SHA1_32: return SRTP_AES128_CM_HMAC_SHA1_32; case BCTBX_SRTP_NULL_HMAC_SHA1_80: return SRTP_NULL_HMAC_SHA1_80; case BCTBX_SRTP_NULL_HMAC_SHA1_32: return SRTP_NULL_HMAC_SHA1_32; default: return SRTP_UNSET_PROFILE; } } bctbx_dtls_srtp_profile_t bctbx_ssl_get_dtls_srtp_protection_profile(bctbx_ssl_context_t *ssl_ctx) { if (ssl_ctx==NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } return bctbx_srtp_profile_polarssl2bctoolbox(ssl_get_dtls_srtp_protection_profile(&(ssl_ctx->ssl_ctx))); }; int32_t bctbx_ssl_get_dtls_srtp_key_material(bctbx_ssl_context_t *ssl_ctx, char *output, size_t *output_length) { if (ssl_ctx==NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } /* check output buffer size */ if (*output_lengthssl_ctx.dtls_srtp_keys_len) { return BCTBX_ERROR_OUTPUT_BUFFER_TOO_SMALL; } memcpy(output, ssl_ctx->ssl_ctx.dtls_srtp_keys, ssl_ctx->ssl_ctx.dtls_srtp_keys_len); *output_length = ssl_ctx->ssl_ctx.dtls_srtp_keys_len; return 0; } #else /* HAVE_DTLS_SRTP */ /* dummy DTLS api when not available */ uint8_t bctbx_dtls_srtp_supported(void) { return 0; } bctbx_dtls_srtp_profile_t bctbx_ssl_get_dtls_srtp_protection_profile(bctbx_ssl_context_t *ssl_ctx) { return BCTBX_SRTP_UNDEFINED; } int32_t bctbx_ssl_get_dtls_srtp_key_material(bctbx_ssl_context_t *ssl_ctx, char *output, size_t *output_length) { *output_length = 0; return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } #endif /* HAVE_DTLS_SRTP */ /** DTLS SRTP functions **/ /** config **/ struct bctbx_ssl_config_struct { int8_t endpoint; /**< BCTBX_SSL_IS_CLIENT or BCTBX_SSL_IS_SERVER */ int8_t authmode; /**< BCTBX_SSL_VERIFY_NONE, BCTBX_SSL_VERIFY_OPTIONAL, BCTBX_SSL_VERIFY_REQUIRED */ int8_t transport; /**< BCTBX_SSL_TRANSPORT_STREAM(TLS) or BCTBX_SSL_TRANSPORT_DATAGRAM(DTLS) */ int(*rng_function)(void *, unsigned char *, size_t); /**< pointer to a random number generator function */ void *rng_context; /**< pointer to a the random number generator context */ int(*callback_verify_function)(void *, x509_crt *, int, int *); /**< pointer to the verify callback function */ void *callback_verify_data; /**< data passed to the verify callback */ x509_crt *ca_chain; /**< trusted CA chain */ x509_crt *own_cert; pk_context *own_cert_pk; int(*callback_cli_cert_function)(void *, bctbx_ssl_context_t *, unsigned char *, size_t); /**< pointer to the callback called to update client certificate during handshake callback params are user_data, ssl_context, certificate distinguished name, name length */ void *callback_cli_cert_data; /**< data passed to the client cert callback */ #ifdef HAVE_DTLS_SRTP enum DTLS_SRTP_protection_profiles dtls_srtp_profiles[4]; /**< Store a maximum of 4 DTLS SRTP profiles to use */ int dtls_srtp_profiles_number; /**< Number of DTLS SRTP profiles in use */ #endif }; bctbx_ssl_config_t *bctbx_ssl_config_new(void) { #ifdef HAVE_DTLS_SRTP int i; #endif bctbx_ssl_config_t *ssl_config = bctbx_malloc0(sizeof(bctbx_ssl_config_t)); /* set all properties to BCTBX_SSL_UNSET or NULL */ ssl_config->endpoint = BCTBX_SSL_UNSET; ssl_config->authmode = BCTBX_SSL_UNSET; ssl_config->transport = BCTBX_SSL_UNSET; ssl_config->rng_function = NULL; ssl_config->rng_context = NULL; ssl_config->callback_verify_function= NULL; ssl_config->callback_verify_data = NULL; ssl_config->callback_cli_cert_function = NULL; ssl_config->callback_cli_cert_data = NULL; ssl_config->ca_chain = NULL; ssl_config->own_cert = NULL; ssl_config->own_cert_pk = NULL; #ifdef HAVE_DTLS_SRTP for (i=0; i<4; i++) { ssl_config->dtls_srtp_profiles[i] = SRTP_UNSET_PROFILE; } ssl_config->dtls_srtp_profiles_number = 0; #endif return ssl_config; } int32_t bctbx_ssl_config_set_crypto_library_config(bctbx_ssl_config_t *ssl_config, void *internal_config) { return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } void bctbx_ssl_config_free(bctbx_ssl_config_t *ssl_config) { bctbx_free(ssl_config); } int32_t bctbx_ssl_config_defaults(bctbx_ssl_config_t *ssl_config, int endpoint, int transport) { if (ssl_config != NULL) { if (endpoint == BCTBX_SSL_IS_CLIENT) { ssl_config->endpoint = SSL_IS_CLIENT; } else if (endpoint == BCTBX_SSL_IS_SERVER) { ssl_config->endpoint = SSL_IS_SERVER; } else { return BCTBX_ERROR_INVALID_SSL_ENDPOINT; } /* useful only for versions of polarssl which have SSL_TRANSPORT_XXX defined */ #ifdef SSL_TRANSPORT_DATAGRAM if (transport == BCTBX_SSL_TRANSPORT_STREAM) { ssl_config->transport = SSL_TRANSPORT_STREAM; } else if (transport == BCTBX_SSL_TRANSPORT_DATAGRAM) { ssl_config->transport = SSL_TRANSPORT_DATAGRAM; } else { return BCTBX_ERROR_INVALID_SSL_TRANSPORT; } #endif return 0; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_endpoint(bctbx_ssl_config_t *ssl_config, int endpoint) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } if (endpoint == BCTBX_SSL_IS_CLIENT) { ssl_config->endpoint = SSL_IS_CLIENT; } else if (endpoint == BCTBX_SSL_IS_SERVER) { ssl_config->endpoint = SSL_IS_SERVER; } else { return BCTBX_ERROR_INVALID_SSL_ENDPOINT; } return 0; } int32_t bctbx_ssl_config_set_transport (bctbx_ssl_config_t *ssl_config, int transport) { if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* useful only for versions of polarssl which have SSL_TRANSPORT_XXX defined */ #ifdef SSL_TRANSPORT_DATAGRAM if (transport == BCTBX_SSL_TRANSPORT_STREAM) { ssl_config->transport = SSL_TRANSPORT_STREAM; } else if (transport == BCTBX_SSL_TRANSPORT_DATAGRAM) { ssl_config->transport = SSL_TRANSPORT_DATAGRAM; } else { return BCTBX_ERROR_INVALID_SSL_TRANSPORT; } #endif return 0; } int32_t bctbx_ssl_config_set_ciphersuites(bctbx_ssl_config_t *ssl_config, const int *ciphersuites) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* unavailable function */ void *bctbx_ssl_config_get_private_config(bctbx_ssl_config_t *ssl_config) { return NULL; } int32_t bctbx_ssl_config_set_authmode(bctbx_ssl_config_t *ssl_config, int authmode) { if (ssl_config != NULL) { switch (authmode) { case BCTBX_SSL_VERIFY_NONE: ssl_config->authmode = SSL_VERIFY_NONE; break; case BCTBX_SSL_VERIFY_OPTIONAL: ssl_config->authmode = SSL_VERIFY_OPTIONAL; break; case BCTBX_SSL_VERIFY_REQUIRED: ssl_config->authmode = SSL_VERIFY_REQUIRED; break; default: return BCTBX_ERROR_INVALID_SSL_AUTHMODE; break; } return 0; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_rng(bctbx_ssl_config_t *ssl_config, int(*rng_function)(void *, unsigned char *, size_t), void *rng_context) { if (ssl_config != NULL) { ssl_config->rng_function = rng_function; ssl_config->rng_context = rng_context; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_callback_verify(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_x509_certificate_t *, int, uint32_t *), void *callback_data) { if (ssl_config != NULL) { ssl_config->callback_verify_function = (int(*)(void *, x509_crt *, int, int *))callback_function; ssl_config->callback_verify_data = callback_data; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_callback_cli_cert(bctbx_ssl_config_t *ssl_config, int(*callback_function)(void *, bctbx_ssl_context_t *, unsigned char *, size_t), void *callback_data) { if (ssl_config != NULL) { ssl_config->callback_cli_cert_function = callback_function; ssl_config->callback_cli_cert_data = callback_data; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_ca_chain(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *ca_chain) { if (ssl_config != NULL) { ssl_config->ca_chain = (x509_crt *)ca_chain; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } int32_t bctbx_ssl_config_set_own_cert(bctbx_ssl_config_t *ssl_config, bctbx_x509_certificate_t *cert, bctbx_signing_key_t *key) { if (ssl_config != NULL) { ssl_config->own_cert = (x509_crt *)cert; ssl_config->own_cert_pk = (pk_context *)key; } return BCTBX_ERROR_INVALID_SSL_CONFIG; } /** DTLS SRTP functions **/ #ifdef HAVE_DTLS_SRTP int32_t bctbx_ssl_config_set_dtls_srtp_protection_profiles(bctbx_ssl_config_t *ssl_config, const bctbx_dtls_srtp_profile_t *profiles, size_t profiles_number) { size_t i; if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } /* convert the profiles array into a polarssl profiles array */ for (i=0; idtls_srtp_profiles[i] = bctbx_srtp_profile_bctoolbox2polarssl(profiles[i]); } for (;i<4; i++) { /* make sure to have harmless values in the rest of the array */ ssl_config->dtls_srtp_profiles[i] = SRTP_UNSET_PROFILE; } ssl_config->dtls_srtp_profiles_number = profiles_number; return 0; } #else /* HAVE_DTLS_SRTP */ int32_t bctbx_ssl_config_set_dtls_srtp_protection_profiles(bctbx_ssl_config_t *ssl_config, const bctbx_dtls_srtp_profile_t *profiles, size_t profiles_number) { return BCTBX_ERROR_UNAVAILABLE_FUNCTION; } #endif /* HAVE_DTLS_SRTP */ /** DTLS SRTP functions **/ int32_t bctbx_ssl_context_setup(bctbx_ssl_context_t *ssl_ctx, bctbx_ssl_config_t *ssl_config) { /* Check validity of context and config */ if (ssl_config == NULL) { return BCTBX_ERROR_INVALID_SSL_CONFIG; } if (ssl_ctx == NULL) { return BCTBX_ERROR_INVALID_SSL_CONTEXT; } /* apply all valids settings to the ssl_context */ if (ssl_config->endpoint != BCTBX_SSL_UNSET) { ssl_set_endpoint(&(ssl_ctx->ssl_ctx), ssl_config->endpoint); } if (ssl_config->authmode != BCTBX_SSL_UNSET) { ssl_set_authmode(&(ssl_ctx->ssl_ctx), ssl_config->authmode); } #ifdef HAVE_DTLS_SRTP if (ssl_config->transport != BCTBX_SSL_UNSET) { ssl_set_transport(&(ssl_ctx->ssl_ctx), ssl_config->transport); } #endif if (ssl_config->rng_function != NULL) { ssl_set_rng(&(ssl_ctx->ssl_ctx), ssl_config->rng_function, ssl_config->rng_context); } if (ssl_config->callback_verify_function != NULL) { ssl_set_verify(&(ssl_ctx->ssl_ctx), ssl_config->callback_verify_function, ssl_config->callback_verify_data); } if (ssl_config->callback_cli_cert_function != NULL) { ssl_ctx->callback_cli_cert_function = ssl_config->callback_cli_cert_function; ssl_ctx->callback_cli_cert_data = ssl_config->callback_cli_cert_data; } if (ssl_config->ca_chain != NULL) { ssl_set_ca_chain(&(ssl_ctx->ssl_ctx), ssl_config->ca_chain, NULL, ssl_ctx->cn); } if (ssl_config->own_cert != NULL && ssl_config->own_cert_pk != NULL) { ssl_set_own_cert(&(ssl_ctx->ssl_ctx) , ssl_config->own_cert , ssl_config->own_cert_pk); } #ifdef HAVE_DTLS_SRTP if (ssl_config->dtls_srtp_profiles_number > 0) { ssl_set_dtls_srtp_protection_profiles(&(ssl_ctx->ssl_ctx), ssl_config->dtls_srtp_profiles, ssl_config->dtls_srtp_profiles_number ); } /* We do not use DTLS SRTP cookie, so we must set to NULL the callbacks. Cookies are used to prevent DoS attack but our server is on only when during a brief period so we do not need this */ ssl_set_dtls_cookies(&(ssl_ctx->ssl_ctx), NULL, NULL, NULL); #endif return 0; } /*****************************************************************************/ /***** Hashing *****/ /*****************************************************************************/ /** * @brief HMAC-SHA512 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length in bytes * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha512(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[64]; sha512_hmac(key, keyLength, input, inputLength, hmacOutput, 0); /* last param to one to select SHA512 and not SHA384 */ /* check output length, can't be>64 */ if (hmacLength>64) { memcpy(output, hmacOutput, 64); } else { memcpy(output, hmacOutput, hmacLength); } } /** * @brief HMAC-SHA384 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length in bytes * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha384(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[48]; sha512_hmac(key, keyLength, input, inputLength, hmacOutput, 1); /* last param to one to select SHA384 and not SHA512 */ /* check output length, can't be>48 */ if (hmacLength>48) { memcpy(output, hmacOutput, 48); } else { memcpy(output, hmacOutput, hmacLength); } } /** * @brief HMAC-SHA256 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length in bytes * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_hmacSha256(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[32]; sha256_hmac(key, keyLength, input, inputLength, hmacOutput, 0); /* last param to zero to select SHA256 and not SHA224 */ /* check output length, can't be>32 */ if (hmacLength>32) { memcpy(output, hmacOutput, 32); } else { memcpy(output, hmacOutput, hmacLength); } } /* * @brief SHA512 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 64 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha512(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[64]; sha512(input, inputLength, hashOutput, 0); /* last param to zero to select SHA512 and not SHA384 */ /* check output length, can't be>64 */ if (hashLength>64) { memcpy(output, hashOutput, 64); } else { memcpy(output, hashOutput, hashLength); } } /* * @brief SHA384 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hashLength Length of output required in bytes, Output is truncated to the hashLength left bytes. 48 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha384(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[48]; sha512(input, inputLength, hashOutput, 1); /* last param to one to select SHA384 and not SHA512 */ /* check output length, can't be>48 */ if (hashLength>48) { memcpy(output, hashOutput, 48); } else { memcpy(output, hashOutput, hashLength); } } /** * @brief SHA256 wrapper * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[in] hmacLength Length of output required in bytes, SHA256 output is truncated to the hashLength left bytes. 32 bytes maximum * @param[out] output Output data buffer. * */ void bctbx_sha256(const uint8_t *input, size_t inputLength, uint8_t hashLength, uint8_t *output) { uint8_t hashOutput[32]; sha256(input, inputLength, hashOutput, 0); /* last param to zero to select SHA256 and not SHA224 */ /* check output length, can't be>32 */ if (hashLength>32) { memcpy(output, hashOutput, 32); } else { memcpy(output, hashOutput, hashLength); } } /** * @brief HMAC-SHA1 wrapper * @param[in] key HMAC secret key * @param[in] keyLength HMAC key length * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[in] hmacLength Length of output required in bytes, HMAC output is truncated to the hmacLength left bytes. 20 bytes maximum * @param[out] output Output data buffer * */ void bctbx_hmacSha1(const uint8_t *key, size_t keyLength, const uint8_t *input, size_t inputLength, uint8_t hmacLength, uint8_t *output) { uint8_t hmacOutput[20]; sha1_hmac(key, keyLength, input, inputLength, hmacOutput); /* check output length, can't be>20 */ if (hmacLength>20) { memcpy(output, hmacOutput, 20); } else { memcpy(output, hmacOutput, hmacLength); } } /** * @brief MD5 wrapper * output = md5(input) * @param[in] input Input data buffer * @param[in] inputLength Input data length in bytes * @param[out] output Output data buffer. * */ void bctbx_md5(const uint8_t *input, size_t inputLength, uint8_t output[16]) { md5(input, inputLength, output); } /*****************************************************************************/ /***** Encryption/Decryption *****/ /*****************************************************************************/ /***** GCM *****/ /** * @Brief AES-GCM encrypt and tag buffer * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] plainText Buffer to be encrypted * @param[in] plainTextLength Length in bytes of buffer to be encrypted * @param[in] authenticatedData Buffer holding additional data to be used in tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[out] tag Buffer holding the generated tag * @param[in] tagLength Requested length for the generated tag * @param[out] output Buffer holding the output, shall be at least the length of plainText buffer */ int32_t bctbx_aes_gcm_encrypt_and_tag(const uint8_t *key, size_t keyLength, const uint8_t *plainText, size_t plainTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t *tag, size_t tagLength, uint8_t *output) { gcm_context gcmContext; int ret; ret = gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key, keyLength*8); if (ret != 0) return ret; ret = gcm_crypt_and_tag(&gcmContext, GCM_ENCRYPT, plainTextLength, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength, plainText, output, tagLength, tag); gcm_free(&gcmContext); return ret; } /** * @Brief AES-GCM decrypt, compute authentication tag and compare it to the one provided * * @param[in] key Encryption key * @param[in] keyLength Key buffer length, in bytes, must be 16,24 or 32 * @param[in] cipherText Buffer to be decrypted * @param[in] cipherTextLength Length in bytes of buffer to be decrypted * @param[in] authenticatedData Buffer holding additional data to be used in auth tag computation * @param[in] authenticatedDataLength Additional data length in bytes * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] tag Buffer holding the authentication tag * @param[in] tagLength Length in bytes for the authentication tag * @param[out] output Buffer holding the output, shall be at least the length of cipherText buffer * * @return 0 on succes, BCTBX_ERROR_AUTHENTICATION_FAILED if tag doesn't match or polarssl error code */ int32_t bctbx_aes_gcm_decrypt_and_auth(const uint8_t *key, size_t keyLength, const uint8_t *cipherText, size_t cipherTextLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, const uint8_t *tag, size_t tagLength, uint8_t *output) { gcm_context gcmContext; int ret; ret = gcm_init(&gcmContext, POLARSSL_CIPHER_ID_AES, key, keyLength*8); if (ret != 0) return ret; ret = gcm_auth_decrypt(&gcmContext, cipherTextLength, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength, tag, tagLength, cipherText, output); gcm_free(&gcmContext); if (ret == POLARSSL_ERR_GCM_AUTH_FAILED) { return BCTBX_ERROR_AUTHENTICATION_FAILED; } return ret; } /** * @Brief create and initialise an AES-GCM encryption context * * @param[in] key encryption key * @param[in] keyLength key buffer length, in bytes, must be 16,24 or 32 * @param[in] authenticatedData Buffer holding additional data to be used in tag computation (can be NULL) * @param[in] authenticatedDataLength additional data length in bytes (can be 0) * @param[in] initializationVector Buffer holding the initialisation vector * @param[in] initializationVectorLength Initialisation vector length in bytes * @param[in] mode Operation mode : BCTBX_GCM_ENCRYPT or BCTBX_GCM_DECRYPT * * @return 0 on success, crypto library error code otherwise */ bctbx_aes_gcm_context_t *bctbx_aes_gcm_context_new(const uint8_t *key, size_t keyLength, const uint8_t *authenticatedData, size_t authenticatedDataLength, const uint8_t *initializationVector, size_t initializationVectorLength, uint8_t mode) { int ret = 0; int polarssl_mode; gcm_context *ctx = NULL; if (mode == BCTBX_GCM_ENCRYPT) { polarssl_mode = GCM_ENCRYPT; } else if ( mode == BCTBX_GCM_DECRYPT) { polarssl_mode = GCM_DECRYPT; } else { return NULL; } ctx = bctbx_malloc0(sizeof(gcm_context)); ret = gcm_init(ctx, POLARSSL_CIPHER_ID_AES, key, keyLength*8); if (ret != 0) { bctbx_free(ctx); return NULL; } ret = gcm_starts(ctx, polarssl_mode, initializationVector, initializationVectorLength, authenticatedData, authenticatedDataLength); if (ret != 0) { bctbx_free(ctx); return NULL; } return (bctbx_aes_gcm_context_t *)ctx; } /** * @Brief AES-GCM encrypt or decrypt a chunk of data * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[in] input buffer holding the input data * @param[in] inputLength lenght of the input data * @param[out] output buffer to store the output data (same length as input one) * * @return 0 on success, crypto library error code otherwise */ int32_t bctbx_aes_gcm_process_chunk(bctbx_aes_gcm_context_t *context, const uint8_t *input, size_t inputLength, uint8_t *output) { return gcm_update((gcm_context *)context, inputLength, input, output); } /** * @Brief Conclude a AES-GCM encryption stream, generate tag if requested, free resources * * @param[in/out] context a context already initialized using bctbx_aes_gcm_context_new * @param[out] tag a buffer to hold the authentication tag. Can be NULL if tagLength is 0 * @param[in] tagLength length of reqested authentication tag, max 16 * * @return 0 on success, crypto library error code otherwise */ int32_t bctbx_aes_gcm_finish(bctbx_aes_gcm_context_t *context, uint8_t *tag, size_t tagLength) { int ret; ret = gcm_finish((gcm_context *)context, tag, tagLength); gcm_free((gcm_context *)context); bctbx_free(context); return ret; } /* * @brief Wrapper for AES-128 in CFB128 mode encryption * Both key and IV must be 16 bytes long, IV is not updated * * @param[in] key encryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes128CfbEncrypt(const uint8_t key[16], const uint8_t IV[16], const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; /* is not used by us but needed and updated by polarssl */ aes_context context; memset (&context, 0, sizeof(aes_context)); /* make a local copy of IV which is modified by the polar ssl AES-CFB function */ memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); /* initialise the aes context and key */ aes_setkey_enc(&context, key, 128); /* encrypt */ aes_crypt_cfb128 (&context, AES_ENCRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-128 in CFB128 mode decryption * Both key and IV must be 16 bytes long, IV is not updated * * @param[in] key decryption key, 128 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes128CfbDecrypt(const uint8_t key[16], const uint8_t IV[16], const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; /* is not used by us but needed and updated by polarssl */ aes_context context; memset (&context, 0, sizeof(aes_context)); /* make a local copy of IV which is modified by the polar ssl AES-CFB function */ memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); /* initialise the aes context and key - use the aes_setkey_enc function as requested by the documentation of aes_crypt_cfb128 function */ aes_setkey_enc(&context, key, 128); /* encrypt */ aes_crypt_cfb128 (&context, AES_DECRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-256 in CFB128 mode encryption * The key must be 32 bytes long and the IV must be 16 bytes long, IV is not updated * * @param[in] key encryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes256CfbEncrypt(const uint8_t key[32], const uint8_t IV[16], const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; aes_context context; memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); memset (&context, 0, sizeof(aes_context)); aes_setkey_enc(&context, key, 256); /* encrypt */ aes_crypt_cfb128 (&context, AES_ENCRYPT, inputLength, &iv_offset, IVbuffer, input, output); } /* * @brief Wrapper for AES-256 in CFB128 mode decryption * The key must be 32 bytes long and the IV must be 16 bytes long, IV is not updated * * @param[in] key decryption key, 256 bits long * @param[in] IV Initialisation vector, 128 bits long, is not modified by this function. * @param[in] input Input data buffer * @param[in] inputLength Input data length * @param[out] output Output data buffer * */ void bctbx_aes256CfbDecrypt(const uint8_t key[32], const uint8_t IV[16], const uint8_t *input, size_t inputLength, uint8_t *output) { uint8_t IVbuffer[16]; size_t iv_offset=0; aes_context context; memcpy(IVbuffer, IV, 16*sizeof(uint8_t)); memset (&context, 0, sizeof(aes_context)); aes_setkey_enc(&context, key, 256); /* decrypt */ aes_crypt_cfb128 (&context, AES_DECRYPT, inputLength, &iv_offset, IVbuffer, input, output); } bctoolbox-5.2.0/src/logging/000077500000000000000000000000001434566643100157425ustar00rootroot00000000000000bctoolbox-5.2.0/src/logging/logging.c000066400000000000000000000615231434566643100175430ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/logging.h" #ifdef _WIN32 extern void setStackTraceHooks(); #endif #include #include #include #include #ifdef _MSC_VER #ifndef access #define access _access #endif #ifndef fileno #define fileno _fileno #endif #endif /* * Exclude windows and android for bctbx_set_thread_log_level() implementation. * Android has lots of bugs around thread local storage and JVM. */ #if !defined(_WIN32) && !defined(__ANDROID__) #define THREAD_LOG_LEVEL_ENABLED 1 #endif #ifdef __ANDROID__ #include #endif /* __ANDROID__ */ typedef struct{ char *domain; unsigned int logmask; #ifdef THREAD_LOG_LEVEL_ENABLED int thread_level_set; /* Set to 1 if a thread specific log level has been set. This enables an optimisation only for the case of an app that never uses per-thread log levels.*/ pthread_key_t thread_level_key; /* The key to access the thread specific level. */ #endif }BctoolboxLogDomain; #ifdef THREAD_LOG_LEVEL_ENABLED static void thread_level_key_destroy(void *ptr){ bctbx_free(ptr); } #endif static BctoolboxLogDomain * bctbx_log_domain_new(const char *domain, unsigned int logmask){ BctoolboxLogDomain *ld = bctbx_new0(BctoolboxLogDomain, 1); ld->domain = domain ? bctbx_strdup(domain) : NULL; ld->logmask = logmask; #ifdef THREAD_LOG_LEVEL_ENABLED ld->thread_level_set = FALSE; pthread_key_create(&ld->thread_level_key, thread_level_key_destroy); #endif return ld; } void bctbx_log_domain_destroy(BctoolboxLogDomain *obj){ #if THREAD_LOG_LEVEL_ENABLED pthread_key_delete(obj->thread_level_key); #endif if (obj->domain) bctbx_free(obj->domain); bctbx_free(obj); } #if THREAD_LOG_LEVEL_ENABLED unsigned int bctbx_log_domain_get_thread_log_level_mask(BctoolboxLogDomain *ld){ unsigned int *specific = (unsigned int*)pthread_getspecific(ld->thread_level_key); if (!specific) return 0; return *specific; } #endif typedef struct _bctbx_logger_t { BctoolboxLogDomain *default_log_domain; bctbx_list_t *logv_outs; unsigned long log_thread_id; bctbx_list_t *log_stored_messages_list; bctbx_list_t *log_domains; bctbx_mutex_t log_stored_messages_mutex; bctbx_mutex_t domains_mutex; bctbx_mutex_t log_mutex; bctbx_log_handler_t * default_handler; } bctbx_logger_t; struct _bctbx_log_handler_t { BctbxLogHandlerFunc func; BctbxLogHandlerDestroyFunc destroy; char *domain; /*domain this log handler is limited to. NULL for all*/ void* user_info; }; typedef struct _bctbx_file_log_handler_t { char* path; char* name; uint64_t max_size; uint64_t size; FILE* file; bool_t reopen_requested; } bctbx_file_log_handler_t; void bctbx_logv_out_cb(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args); static void wrapper(void* info,const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) { BctbxLogFunc func = (BctbxLogFunc)info; if (func) func(domain, lev, fmt, args); } static bctbx_logger_t main_logger = {0}; static bctbx_log_handler_t static_handler = {0}; static void initialize_default_handler(void) { main_logger.default_handler = &static_handler; static_handler.func = wrapper; static_handler.destroy = (BctbxLogHandlerDestroyFunc)bctbx_logv_out_destroy; static_handler.user_info = (void*)bctbx_logv_out; bctbx_add_log_handler(&static_handler); } static bctbx_logger_t* bctbx_get_logger(void) { if (main_logger.default_log_domain == NULL) { main_logger.default_log_domain = bctbx_log_domain_new(NULL, BCTBX_LOG_WARNING | BCTBX_LOG_ERROR | BCTBX_LOG_FATAL); bctbx_mutex_init(&main_logger.domains_mutex, NULL); bctbx_mutex_init(&main_logger.log_mutex, NULL); #if ENABLE_DEFAULT_LOG_HANDLER initialize_default_handler(); #endif } return &main_logger; } void bctbx_init_logger(bool_t create) { bctbx_get_logger(); } void bctbx_log_handlers_free(void) { bctbx_list_t *handlers = bctbx_list_first_elem(bctbx_get_logger()->logv_outs); while (handlers) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)handlers->data; handler->destroy(handler); handlers = handlers->next; } } void bctbx_uninit_logger(void){ /* Calling bctbx_uninit_logger() from a component is dangerous as it will reset the default logger. * it is safer that this function does nothing.*/ #if 0 bctbx_logger_t * logger = bctbx_get_logger(); bctbx_logv_flush(); bctbx_mutex_destroy(&logger->domains_mutex); bctbx_mutex_destroy(&logger->log_mutex); bctbx_log_handlers_free(); logger->logv_outs = bctbx_list_free(logger->logv_outs); logger->log_domains = bctbx_list_free_with_data(logger->log_domains, (void (*)(void*))bctbx_log_domain_destroy); bctbx_log_domain_destroy(logger->default_log_domain); logger->default_log_domain = NULL; #endif } bctbx_log_handler_t* bctbx_create_log_handler(BctbxLogHandlerFunc func, BctbxLogHandlerDestroyFunc destroy, void* user_info) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)bctbx_malloc0(sizeof(bctbx_log_handler_t)); handler->func = func; handler->destroy = destroy; handler->user_info = user_info; return handler; } void bctbx_log_handler_set_user_data(bctbx_log_handler_t* log_handler, void* user_data) { log_handler->user_info = user_data; } void *bctbx_log_handler_get_user_data(const bctbx_log_handler_t* log_handler) { return log_handler->user_info; } void bctbx_log_handler_set_domain(bctbx_log_handler_t * log_handler, const char *domain) { if (log_handler->domain) bctbx_free(log_handler->domain); if (domain) { log_handler->domain = bctbx_strdup(domain); } else { log_handler->domain = NULL ; } } bctbx_log_handler_t* bctbx_create_file_log_handler(uint64_t max_size, const char* path, const char* name) { bctbx_log_handler_t *handler = NULL; bctbx_file_log_handler_t *filehandler = NULL; char *full_name = bctbx_strdup_printf("%s/%s", path, name); struct stat buf = {0}; FILE *f = fopen(full_name, "a"); if (f == NULL) { fprintf(stderr, "error while opening '%s': %s\n", full_name, strerror(errno)); goto end; } if (stat(full_name, &buf) != 0) { fprintf(stderr, "error while gathering info about '%s': %s", full_name, strerror(errno)); goto end; } filehandler = bctbx_new0(bctbx_file_log_handler_t, 1); filehandler->max_size = max_size; filehandler->size = buf.st_size; filehandler->path = bctbx_strdup(path); filehandler->name = bctbx_strdup(name); filehandler->file = f; handler = bctbx_new0(bctbx_log_handler_t, 1); handler->func = bctbx_logv_file; handler->destroy = bctbx_logv_file_destroy; handler->user_info = filehandler; end: bctbx_free(full_name); return handler; } void bctbx_file_log_handler_reopen(bctbx_log_handler_t *file_log_handler) { bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *)file_log_handler->user_info; bctbx_logger_t *logger = bctbx_get_logger(); bctbx_mutex_lock(&logger->log_mutex); filehandler->reopen_requested = TRUE; bctbx_mutex_unlock(&logger->log_mutex); } /** *@param func: your logging function, compatible with the BctoolboxLogFunc prototype. * **/ void bctbx_add_log_handler(bctbx_log_handler_t* handler){ bctbx_logger_t *logger = bctbx_get_logger(); if (handler && !bctbx_list_find(logger->logv_outs, handler)) logger->logv_outs = bctbx_list_append(logger->logv_outs, (void*)handler); /*else, already in*/ } void bctbx_remove_log_handler(bctbx_log_handler_t* handler){ bctbx_logger_t *logger = bctbx_get_logger(); logger->logv_outs = bctbx_list_remove(logger->logv_outs, handler); handler->destroy(handler); return; } void bctbx_set_log_handler(BctbxLogFunc func){ bctbx_set_log_handler_for_domain(func,NULL); } void bctbx_set_log_handler_for_domain(BctbxLogFunc func, const char* domain) { bctbx_log_handler_t* h = bctbx_get_logger()->default_handler; if (h == NULL) { initialize_default_handler(); h = bctbx_get_logger()->default_handler; } h->user_info = (void*)func; bctbx_log_handler_set_domain(h, domain); } void bctbx_set_log_file(FILE* f){ static bctbx_file_log_handler_t filehandler; static bctbx_log_handler_t handler; handler.func=bctbx_logv_file; handler.destroy=(BctbxLogHandlerDestroyFunc)bctbx_logv_file_destroy; filehandler.max_size = -1; filehandler.file = f; handler.user_info=(void*) &filehandler; bctbx_add_log_handler(&handler); } bctbx_list_t* bctbx_get_log_handlers(void){ return bctbx_get_logger()->logv_outs; } static BctoolboxLogDomain * get_log_domain(const char *domain){ bctbx_list_t *it; bctbx_logger_t *logger = bctbx_get_logger(); if (domain == NULL) return logger->default_log_domain; for (it = logger->log_domains; it != NULL; it = bctbx_list_next(it)) { BctoolboxLogDomain *ld = (BctoolboxLogDomain*)bctbx_list_get_data(it); if (ld->domain && strcmp(ld->domain, domain) == 0 ){ return ld; } } return NULL; } static BctoolboxLogDomain *get_log_domain_rw(const char *domain){ BctoolboxLogDomain *ret; bctbx_logger_t *logger = bctbx_get_logger(); ret = get_log_domain(domain); if (ret) return ret; /*it does not exist, hence create it by taking the mutex*/ bctbx_mutex_lock(&logger->domains_mutex); ret = get_log_domain(domain); if (!ret){ ret = bctbx_log_domain_new(domain, logger->default_log_domain->logmask); logger->log_domains = bctbx_list_prepend(logger->log_domains, ret); } bctbx_mutex_unlock(&logger->domains_mutex); return ret; } /** * @ param levelmask a mask of BCTBX_DEBUG, BCTBX_MESSAGE, BCTBX_WARNING, BCTBX_ERROR * BCTBX_FATAL . **/ void bctbx_set_log_level_mask(const char *domain, int levelmask){ get_log_domain_rw(domain)->logmask = levelmask; } static unsigned int level_to_mask(BctbxLogLevel level){ unsigned int levelmask = BCTBX_LOG_FATAL; if (level<=BCTBX_LOG_ERROR){ levelmask |= BCTBX_LOG_ERROR; } if (level<=BCTBX_LOG_WARNING){ levelmask |= BCTBX_LOG_WARNING; } if (level<=BCTBX_LOG_MESSAGE){ levelmask |= BCTBX_LOG_MESSAGE; } if (level<=BCTBX_LOG_TRACE) { levelmask |= BCTBX_LOG_TRACE; } if (level<=BCTBX_LOG_DEBUG){ levelmask |= BCTBX_LOG_DEBUG; } return levelmask; } void bctbx_set_log_level(const char *domain, BctbxLogLevel level){ bctbx_set_log_level_mask(domain, level_to_mask(level)); } unsigned int bctbx_get_log_level_mask(const char *domain) { BctoolboxLogDomain *ld = get_log_domain(domain); if (!ld) ld = bctbx_get_logger()->default_log_domain; return ld->logmask; } int bctbx_log_level_enabled(const char *domain, BctbxLogLevel level){ unsigned int logmask = 0; BctoolboxLogDomain *ld = get_log_domain(domain); if (!ld) ld = bctbx_get_logger()->default_log_domain; #ifdef THREAD_LOG_LEVEL_ENABLED if (ld->thread_level_set) logmask = bctbx_log_domain_get_thread_log_level_mask(ld); #endif if (logmask == 0) logmask = ld->logmask; /* if there is no thread specific log level, revert to global log level.*/ return (logmask & (unsigned int)level) != 0; } void bctbx_set_log_thread_id(unsigned long thread_id) { bctbx_logger_t *logger = bctbx_get_logger(); if (thread_id == 0) { bctbx_logv_flush(); bctbx_mutex_destroy(&logger->log_stored_messages_mutex); } else { bctbx_mutex_init(&logger->log_stored_messages_mutex, NULL); } logger->log_thread_id = thread_id; } char * bctbx_strdup_vprintf(const char *fmt, va_list ap) { /* Guess we need no more than 100 bytes. */ int n, size = 200; char *p,*np; #ifndef _WIN32 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/ #endif if ((p = (char *) bctbx_malloc (size)) == NULL) return NULL; while (1){ /* Try to print in the allocated space. */ #ifndef _WIN32 va_copy(cap,ap); n = vsnprintf (p, size, fmt, cap); va_end(cap); #else /*this works on 32 bits, luckily*/ n = vsnprintf (p, size, fmt, ap); #endif /* If that worked, return the string. */ if (n > -1 && n < size) return p; //printf("Reallocing space.\n"); /* Else try again with more space. */ if (n > -1) /* glibc 2.1 */ size = n + 1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ if ((np = (char *) bctbx_realloc (p, size)) == NULL) { free(p); return NULL; } else { p = np; } } } char *bctbx_strdup_printf(const char *fmt,...){ char *ret; va_list args; va_start (args, fmt); ret=bctbx_strdup_vprintf(fmt, args); va_end (args); return ret; } char * bctbx_strcat_vprintf(char* dst, const char *fmt, va_list ap){ char *ret; size_t dstlen, retlen; ret=bctbx_strdup_vprintf(fmt, ap); if (!dst) return ret; dstlen = strlen(dst); retlen = strlen(ret); if ((dst = bctbx_realloc(dst, dstlen+retlen+1)) != NULL){ strcat(dst,ret); bctbx_free(ret); return dst; } else { bctbx_free(ret); return NULL; } } char *bctbx_strcat_printf(char* dst, const char *fmt,...){ char *ret; va_list args; va_start (args, fmt); ret=bctbx_strcat_vprintf(dst, fmt, args); va_end (args); return ret; } #if !defined(BCTBX_WINDOWS_UWP) && (defined(_WIN32) || defined(_WIN32_WCE)) #define ENDLINE "\r\n" #else #define ENDLINE "\n" #endif typedef struct { int level; char *msg; char *domain; } bctbx_stored_log_t; void _bctbx_logv_flush(int dummy, ...) { bctbx_list_t *elem; bctbx_list_t *msglist; va_list empty_va_list; va_start(empty_va_list, dummy); bctbx_logger_t *logger = bctbx_get_logger(); bctbx_mutex_lock(&logger->log_stored_messages_mutex); msglist = logger->log_stored_messages_list; logger->log_stored_messages_list = NULL; bctbx_mutex_unlock(&logger->log_stored_messages_mutex); for (elem = msglist; elem != NULL; elem = bctbx_list_next(elem)) { bctbx_stored_log_t *l = (bctbx_stored_log_t *)bctbx_list_get_data(elem); bctbx_list_t *handlers = bctbx_list_first_elem(logger->logv_outs); #ifdef _WIN32 while (handlers) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)handlers->data; if(handler) { va_list cap; va_copy(cap, empty_va_list); handler->func(handler->user_info, l->domain, l->level, l->msg, cap); va_end(cap); } handlers = handlers->next; } #else while (handlers) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)handlers->data; if(handler) { va_list cap; va_copy(cap, empty_va_list); handler->func(handler->user_info, l->domain, l->level, l->msg, cap); va_end(cap); } handlers = handlers->next; } #endif if (l->domain) bctbx_free(l->domain); bctbx_free(l->msg); bctbx_free(l); } bctbx_list_free(msglist); va_end(empty_va_list); } void bctbx_logv_flush(void) { _bctbx_logv_flush(0); } void bctbx_logv(const char *domain, BctbxLogLevel level, const char *fmt, va_list args) { bctbx_logger_t *logger = bctbx_get_logger(); if ((logger->logv_outs != NULL) && bctbx_log_level_enabled(domain, level)) { if (logger->log_thread_id == 0) { bctbx_list_t *handlers = bctbx_list_first_elem(logger->logv_outs); while (handlers) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)handlers->data; if(handler && (!handler->domain || !domain || strcmp(handler->domain,domain)==0)) { va_list tmp; va_copy(tmp, args); handler->func(handler->user_info, domain, level, fmt, tmp); va_end(tmp); } handlers = handlers->next; } } else if (logger->log_thread_id == bctbx_thread_self()) { bctbx_list_t *handlers; bctbx_logv_flush(); handlers = bctbx_list_first_elem(logger->logv_outs); while (handlers) { bctbx_log_handler_t* handler = (bctbx_log_handler_t*)handlers->data; if(handler && (!handler->domain || !domain || strcmp(handler->domain,domain)==0)) { va_list tmp; va_copy(tmp, args); handler->func(handler->user_info, domain, level, fmt, tmp); va_end(tmp); } handlers = handlers->next; } } else { bctbx_stored_log_t *l = bctbx_new(bctbx_stored_log_t, 1); l->domain = domain ? bctbx_strdup(domain) : NULL; l->level = level; l->msg = bctbx_strdup_vprintf(fmt, args); bctbx_mutex_lock(&logger->log_stored_messages_mutex); logger->log_stored_messages_list = bctbx_list_append(logger->log_stored_messages_list, l); bctbx_mutex_unlock(&logger->log_stored_messages_mutex); } } #if !defined(_WIN32_WCE) if (level == BCTBX_LOG_FATAL) { bctbx_logv_flush(); #ifdef __ANDROID__ //Act as a flush + abort __android_log_assert(NULL, NULL, "%s", "Aborting"); #else abort(); #endif } #endif } void bctbx_logv_out( const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){ bctbx_logv_out_cb(NULL, domain, lev, fmt, args); } /*This function does the default formatting and output to file*/ void bctbx_logv_out_cb(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){ const char *lname="undef"; char *msg; struct timeval tp; struct tm *lt; #ifndef _WIN32 struct tm tmbuf; #endif time_t tt; FILE *std = stdout; bctbx_gettimeofday(&tp,NULL); tt = (time_t)tp.tv_sec; #ifdef _WIN32 lt = localtime(&tt); #else lt = localtime_r(&tt,&tmbuf); #endif switch(lev){ case BCTBX_LOG_DEBUG: lname = "debug"; break; case BCTBX_LOG_MESSAGE: lname = "message"; break; case BCTBX_LOG_WARNING: lname = "warning"; break; case BCTBX_LOG_ERROR: lname = "error"; std = stderr; break; case BCTBX_LOG_FATAL: lname = "fatal"; std = stderr; break; default: lname = "badlevel"; } msg=bctbx_strdup_vprintf(fmt,args); #if defined(_MSC_VER) && !defined(_WIN32_WCE) #ifndef _UNICODE OutputDebugStringA(msg); OutputDebugStringA("\r\n"); #else { size_t len=strlen(msg); wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t)); mbstowcs(tmp,msg,len); OutputDebugStringW(tmp); OutputDebugStringW(L"\r\n"); bctbx_free(tmp); } #endif #endif fprintf(std,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE ,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec ,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg); fflush(std); bctbx_free(msg); } void bctbx_logv_out_destroy(bctbx_log_handler_t* handler) { handler->user_info=NULL; } static int _try_open_log_collection_file(bctbx_file_log_handler_t *filehandler) { struct stat statbuf; char *log_filename; log_filename = bctbx_strdup_printf("%s/%s", filehandler->path, filehandler->name); filehandler->file = fopen(log_filename, "a"); bctbx_free(log_filename); if (filehandler->file == NULL) return -1; fstat(fileno(filehandler->file), &statbuf); if ((uint64_t)statbuf.st_size > filehandler->max_size) { fclose(filehandler->file); return -1; } filehandler->size = statbuf.st_size; return 0; } static void _rotate_log_collection_files(bctbx_file_log_handler_t *filehandler) { char *log_filename; char *log_filename2; int n = 1; log_filename = bctbx_strdup_printf("%s/%s_1", filehandler->path, filehandler->name); while(access(log_filename, F_OK) != -1) { // file exists n++; bctbx_free(log_filename); log_filename = bctbx_strdup_printf("%s/%s_%d", filehandler->path, filehandler->name, n); } while(n > 1) { bctbx_free(log_filename); log_filename = bctbx_strdup_printf("%s/%s_%d", filehandler->path, filehandler->name, n-1); log_filename2 = bctbx_strdup_printf("%s/%s_%d", filehandler->path, filehandler->name, n); n--; rename(log_filename, log_filename2); bctbx_free(log_filename2); } bctbx_free(log_filename); log_filename = bctbx_strdup_printf("%s/%s", filehandler->path, filehandler->name); log_filename2 = bctbx_strdup_printf("%s/%s_1", filehandler->path, filehandler->name); rename(log_filename, log_filename2); bctbx_free(log_filename); bctbx_free(log_filename2); } static void _open_log_collection_file(bctbx_file_log_handler_t *filehandler) { if (_try_open_log_collection_file(filehandler) < 0) { _rotate_log_collection_files(filehandler); _try_open_log_collection_file(filehandler); } } static void _close_log_collection_file(bctbx_file_log_handler_t *filehandler) { if (filehandler->file) { fclose(filehandler->file); filehandler->file = NULL; filehandler->size = 0; } } void bctbx_logv_file(void* user_info, const char *domain, BctbxLogLevel lev, const char *fmt, va_list args){ const char *lname="undef"; char *msg = NULL; struct timeval tp; struct tm *lt; #ifndef _WIN32 struct tm tmbuf; #endif time_t tt; int ret = -1; bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) user_info; bctbx_logger_t *logger = bctbx_get_logger(); bctbx_mutex_lock(&logger->log_mutex); FILE *f = filehandler ? filehandler->file : stdout; bctbx_gettimeofday(&tp,NULL); tt = (time_t)tp.tv_sec; #ifdef _WIN32 lt = localtime(&tt); #else lt = localtime_r(&tt,&tmbuf); #endif if(!f) goto end; switch(lev){ case BCTBX_LOG_DEBUG: lname = "debug"; break; case BCTBX_LOG_MESSAGE: lname = "message"; break; case BCTBX_LOG_WARNING: lname = "warning"; break; case BCTBX_LOG_ERROR: lname = "error"; break; case BCTBX_LOG_FATAL: lname = "fatal"; break; default: lname = "badlevel"; } msg=bctbx_strdup_vprintf(fmt,args); #if defined(_MSC_VER) && !defined(_WIN32_WCE) #ifndef _UNICODE OutputDebugStringA(msg); OutputDebugStringA("\r\n"); #else { size_t len=strlen(msg); wchar_t *tmp=(wchar_t*)bctbx_malloc0((len+1)*sizeof(wchar_t)); mbstowcs(tmp,msg,len); OutputDebugStringW(tmp); OutputDebugStringW(L"\r\n"); bctbx_free(tmp); } #endif #endif ret = fprintf(f,"%i-%.2i-%.2i %.2i:%.2i:%.2i:%.3i %s-%s-%s" ENDLINE ,1900+lt->tm_year,1+lt->tm_mon,lt->tm_mday,lt->tm_hour,lt->tm_min,lt->tm_sec ,(int)(tp.tv_usec/1000), (domain?domain:"bctoolbox"), lname, msg); fflush(f); /* reopen the log file when either the size limit has been exceeded, or reopen has been required by the user. Reopening a log file that has reached the size limit automatically trigger log rotation while opening. */ if (filehandler) { bool_t reopen_requested = filehandler->reopen_requested; if (filehandler->max_size > 0 && ret > 0) { filehandler->size += ret; reopen_requested = reopen_requested || filehandler->size > filehandler->max_size; } if (reopen_requested) { _close_log_collection_file(filehandler); _open_log_collection_file(filehandler); filehandler->reopen_requested = FALSE; } } end: bctbx_mutex_unlock(&logger->log_mutex); if (msg) bctbx_free(msg); } void bctbx_logv_file_destroy(bctbx_log_handler_t* handler) { bctbx_file_log_handler_t *filehandler = (bctbx_file_log_handler_t *) handler->user_info; fclose(filehandler->file); bctbx_free(filehandler->path); bctbx_free(filehandler->name); bctbx_logv_out_destroy(handler); } void bctbx_set_thread_log_level(const char *domain, BctbxLogLevel level){ #ifdef THREAD_LOG_LEVEL_ENABLED BctoolboxLogDomain * ld = get_log_domain(domain); unsigned int *specific = (unsigned int*)pthread_getspecific(ld->thread_level_key); if (!specific) specific = bctbx_new0(unsigned int, 1); *specific = level_to_mask(level); pthread_setspecific(ld->thread_level_key, specific); ld->thread_level_set = TRUE; #endif } void bctbx_clear_thread_log_level(const char *domain){ #ifdef THREAD_LOG_LEVEL_ENABLED BctoolboxLogDomain * ld = get_log_domain(domain); unsigned int *specific = (unsigned int*)pthread_getspecific(ld->thread_level_key); if (specific) *specific = 0; #endif } #ifdef __QNX__ #include static bool_t slog2_registered = FALSE; static slog2_buffer_set_config_t slog2_buffer_config; static slog2_buffer_t slog2_buffer_handle[2]; void bctbx_qnx_log_handler(const char *domain, BctbxLogLevel lev, const char *fmt, va_list args) { uint8_t severity = SLOG2_DEBUG1; uint8_t buffer_idx = 1; char* msg; if (slog2_registered != TRUE) { slog2_buffer_config.buffer_set_name = domain; slog2_buffer_config.num_buffers = 2; slog2_buffer_config.verbosity_level = SLOG2_DEBUG2; slog2_buffer_config.buffer_config[0].buffer_name = "hi_rate"; slog2_buffer_config.buffer_config[0].num_pages = 6; slog2_buffer_config.buffer_config[1].buffer_name = "lo_rate"; slog2_buffer_config.buffer_config[1].num_pages = 2; if (slog2_register(&slog2_buffer_config, slog2_buffer_handle, 0) == 0) { slog2_registered = TRUE; } else { fprintf(stderr, "Error registering slogger2 buffer!\n"); return; } } switch(lev){ case BCTBX_LOG_DEBUG: severity = SLOG2_DEBUG1; break; case BCTBX_LOG_MESSAGE: severity = SLOG2_INFO; break; case BCTBX_LOG_WARNING: severity = SLOG2_WARNING; break; case BCTBX_LOG_ERROR: severity = SLOG2_ERROR; break; case BCTBX_LOG_FATAL: severity = SLOG2_CRITICAL; break; default: severity = SLOG2_CRITICAL; } msg = bctbx_strdup_vprintf(fmt,args); slog2c(slog2_buffer_handle[buffer_idx], 0, severity, msg); } #endif /* __QNX__ */ bctoolbox-5.2.0/src/param_string.c000066400000000000000000000047621434566643100171570ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox/port.h" #include "bctoolbox/param_string.h" static const char *find_param_occurence_of(const char *fmtp, const char *param){ const char *pos=fmtp; int param_len = (int)strlen(param); do{ pos=strstr(pos,param); if (pos){ /*check that the occurence found is not a subword of a parameter name*/ if (pos==fmtp){ if (pos[param_len] == '=') break; /* found it */ }else if ((pos[-1]==';' || pos[-1]==' ') && pos[param_len] == '='){ break; /* found it */ } pos+=strlen(param); } }while (pos!=NULL); return pos; } static const char *find_last_param_occurence_of(const char *fmtp, const char *param){ const char *pos=fmtp; const char *lastpos=NULL; do{ pos=find_param_occurence_of(pos,param); if (pos) { lastpos=pos; pos+=strlen(param); } }while(pos!=NULL); return lastpos; } bool_t bctbx_param_string_get_value(const char *paramString, const char *param_name, char *result, size_t result_len){ const char *pos=find_last_param_occurence_of(paramString,param_name); memset(result, '\0', result_len); if (pos){ const char *equal=strchr(pos,'='); if (equal){ int copied; const char *end=strchr(equal+1,';'); if (end==NULL) end=paramString+strlen(paramString); /*assuming this is the last param */ copied=MIN((int)(result_len-1),(int)(end-(equal+1))); strncpy(result,equal+1,copied); result[copied]='\0'; return TRUE; } } return FALSE; } bool_t bctbx_param_string_get_bool_value(const char *paramString, const char *param_name) { size_t result_len = 5; char *result = bctbx_malloc(result_len); // True if param is found, false if not bool_t res = bctbx_param_string_get_value(paramString, param_name, result, result_len); res = res && strcmp(result, "true")==0; free(result); return res; } bctoolbox-5.2.0/src/parser.c000066400000000000000000000070451434566643100157620ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/port.h" #include "bctoolbox/parser.h" #include "bctoolbox/logging.h" char* bctbx_escape(const char* buff, const bctbx_noescape_rules_t noescapes) { size_t outbuf_size=strlen(buff); size_t orig_size=outbuf_size; char *output_buff=(char*)bctbx_malloc(outbuf_size + 1); int i; size_t out_buff_index=0; for(i=0; buff[i] != '\0'; i++) { int c = ((unsigned char*)buff)[i]; if (outbuf_size < out_buff_index + 3){ // we will possibly add 3 chars outbuf_size += MAX(orig_size/2,3); output_buff = bctbx_realloc(output_buff, outbuf_size + 1); } if (noescapes[c] == 1) { output_buff[out_buff_index++]=c; } else { // this will write 3 characters out_buff_index+=snprintf(output_buff+out_buff_index, outbuf_size +1 - out_buff_index, "%%%02x", c); } } output_buff[out_buff_index]='\0'; return output_buff; } void bctbx_noescape_rules_add_list(bctbx_noescape_rules_t noescapes, const char *allowed) { while (*allowed) { noescapes[(unsigned int) *allowed] = 1; ++allowed; } } void bctbx_noescape_rules_add_range(bctbx_noescape_rules_t noescapes, char first, char last) { memset(noescapes + (unsigned int)first, 1, last-first+1); } void bctbx_noescape_rules_add_alfanums(bctbx_noescape_rules_t noescapes) { bctbx_noescape_rules_add_range(noescapes, '0', '9'); bctbx_noescape_rules_add_range(noescapes, 'A', 'Z'); bctbx_noescape_rules_add_range(noescapes, 'a', 'z'); } static int is_escaped_char(const char *a){ return a[0] == '%' && a[1] != '\0' && a[2] != '\0'; } size_t bctbx_get_char (const char* a, char* out) { if (is_escaped_char(a)) { unsigned int tmp; sscanf(a + 1, "%02x", &tmp); *out = (char)tmp; return 3; } else { *out = *a; return 1; } } char* bctbx_unescaped_string(const char* buff) { char *output_buff = bctbx_malloc(strlen(buff)+1); size_t i; size_t out_buff_index = 0; for (i = 0; buff[i] != '\0'; out_buff_index++) { i += bctbx_get_char(buff + i, output_buff + out_buff_index); } output_buff[out_buff_index] = '\0'; return output_buff; } char* bctbx_unescaped_string_only_chars_in_rules(const char* buff, const bctbx_noescape_rules_t unescape) { size_t max_len = strlen(buff) + 1; char *output_buff = bctbx_malloc(max_len); size_t i; size_t out_buff_index = 0; for (i = 0; buff[i] != '\0'; ) { i += bctbx_get_char(buff + i, output_buff + out_buff_index); int c = ((unsigned char*)output_buff)[out_buff_index]; if (unescape[c] == 0 && is_escaped_char(buff + i)) { // we unescaped a character that should stay escaped max_len += 3; output_buff = bctbx_realloc(output_buff, max_len); out_buff_index += snprintf(output_buff + out_buff_index, max_len - out_buff_index, "%%%02x", c); } else { out_buff_index += 1; } } output_buff[out_buff_index] = '\0'; return output_buff; }bctoolbox-5.2.0/src/tester.c000066400000000000000000001451461434566643100160010ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #endif #ifndef IN_BCUNIT_SOURCES #include #include #include #include #else #include "Basic.h" #include "Automated.h" #include "MyMem.h" #include "Util.h" #endif #ifdef _WIN32 #if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP) #define BC_TESTER_WINDOWS_DESKTOP 1 #elif defined(WINAPI_FAMILY_PARTITION) #if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define BC_TESTER_WINDOWS_DESKTOP 1 #elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) #define BC_TESTER_WINDOWS_PHONE 1 #elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define BC_TESTER_WINDOWS_UNIVERSAL 1 #endif #endif #endif #ifdef __linux__ /*for monitoring total space allocated via malloc*/ #include #endif #ifndef F_OK #define F_OK 00 /* Visual Studio does not define F_OK */ #endif #ifdef HAVE_BCUNIT_CUCURSES_H #define HAVE_CU_CURSES 1 #endif static char *bc_tester_resource_dir_prefix = NULL; // by default writable will always write near the executable static char *bc_tester_writable_dir_prefix = NULL; static char *bc_current_suite_name = NULL; static char *bc_current_test_name = NULL; static char *log_file_name = NULL; static int bc_printf_verbosity_info; static int bc_printf_verbosity_error; static test_suite_t **test_suite = NULL; static int nb_test_suites = 0; #ifdef HAVE_CU_CURSES #include "BCUnit/CUCurses.h" static unsigned char curses = 0; #endif static const char default_xml_file[] = "BCUnitAutomated"; static const char * xml_file = default_xml_file; static int xml_enabled = 0; static char * suite_name = NULL; static char * test_name = NULL; static char * tag_name = NULL; static char * expected_res = NULL; static size_t max_vm_kb = 0; static int run_skipped_tests = 0; static int max_parallel_suites = 0; /*if 0, but parallel is requested, an arbitrary value is determined.*/ static int run_in_parallel = 0; static uint64_t globalTimeout = 0; static int max_failed_tests_threshold = 0; //To keep record of the process name who started and args static char **origin_argv = NULL; static int origin_argc = 0; //Custom cli arguments handlers for end tests implementations static int (*verbose_arg_func)(const char *arg) = NULL; static int (*silent_arg_func)(const char *arg) = NULL; static int (*logfile_arg_func)(const char *arg) = NULL; //Processing custom native events for processing events (like PeekMessage for Windows) that is not implemented by Linphone static void (*process_events)(void) = NULL; void bc_tester_set_silent_func(int (*func)(const char*)) { if (func) { silent_arg_func = func; } } void bc_tester_set_verbose_func(int (*func)(const char*)) { if (func) { verbose_arg_func = func; } } void bc_tester_set_logfile_func(int (*func)(const char*)) { if (func) { logfile_arg_func = func; } } void bc_tester_set_process_events_func(void (*func)(void)) { process_events = func; } void bc_tester_process_events(){ if( process_events) process_events(); } static void (*tester_printf_va)(int level, const char *format, va_list args)=NULL; void bc_tester_printf(int level, const char *format, ...) { va_list args; va_start (args, format); if (tester_printf_va) { tester_printf_va(level, format, args); } else { vfprintf(stderr, format, args); } va_end (args); } // int bc_tester_register_suite(test_suite_t *suite, const char *tag_name) { int i; CU_pSuite pSuite; if (tag_name != NULL) { size_t j; int nb_tests_for_tag = 0; for (i = 0; i < suite->nb_tests; i++) { for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) { if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) { nb_tests_for_tag++; } } } if (nb_tests_for_tag > 0) { pSuite = CU_add_suite_with_setup_and_teardown(suite->name, suite->before_all, suite->after_all, suite->before_each, suite->after_each); for (i = 0; i < suite->nb_tests; i++) { for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) { if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) { if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) { return CU_get_error(); } } } } } } else { pSuite = CU_add_suite_with_setup_and_teardown(suite->name, suite->before_all, suite->after_all, suite->before_each, suite->after_each); for (i = 0; i < suite->nb_tests; i++) { size_t j; bool_t skip = FALSE; for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) { if ((suite->tests[i].tags[j] != NULL) && (strcasecmp("Skip", suite->tests[i].tags[j]) == 0) && (run_skipped_tests == 0)) { skip = TRUE; } } if (!skip) { if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) { return CU_get_error(); } } } } return 0; } //Allows to register directly to BCUnit by name. Wrap bc_tester_register_suite int bc_tester_register_suite_by_name(const char *suite_name) { int suiteIdx = -1; suiteIdx = bc_tester_suite_index(suite_name); if (suiteIdx != -1) { if (!CU_registry_initialized()) { if (CUE_SUCCESS != CU_initialize_registry()) return CU_get_error(); } return bc_tester_register_suite(test_suite[suiteIdx], NULL); } return -1; } const char * bc_tester_suite_name(int suite_index) { if (suite_index >= nb_test_suites) return NULL; return test_suite[suite_index]->name; } int bc_tester_suite_index(const char *suite_name) { int i; for (i = 0; i < nb_test_suites; i++) { if (strcasecmp(suite_name, test_suite[i]->name) == 0) { return i; } } return -1; } int bc_tester_test_index(test_suite_t *suite, const char *test_name) { int i; for (i = 0; i < suite->nb_tests; i++) { if (strcasecmp(test_name, suite->tests[i].name) == 0) { return i; } } return -1; } int bc_tester_nb_suites(void) { return nb_test_suites; } const char * bc_tester_test_name(const char *suite_name, int test_index) { test_suite_t *suite = NULL; size_t j = 0; bool_t skip = FALSE; int suite_index = bc_tester_suite_index(suite_name); if ((suite_index < 0) || (suite_index >= nb_test_suites)) return NULL; suite = test_suite[suite_index]; if (test_index >= suite->nb_tests) return NULL; for (j = 0; j < (sizeof(suite->tests[test_index].tags) / sizeof(suite->tests[test_index].tags[0])); j++) { if ((suite->tests[test_index].tags[j] != NULL) && (strcasecmp("Skip", suite->tests[test_index].tags[j]) == 0) && (run_skipped_tests == 0)) { skip = TRUE; } } if (skip) return NULL; return suite->tests[test_index].name; } int bc_tester_nb_tests(const char *suite_name) { int i = bc_tester_suite_index(suite_name); if (i < 0) return 0; return test_suite[i]->nb_tests; } void bc_tester_list_suites(void) { int j; for(j=0;jpNext, i++) { tmp = bc_sprintf("%s\n %d. %s:%u - %s", buffer, i, (NULL != pFailure->strFileName) ? pFailure->strFileName : "", pFailure->uiLineNumber, (NULL != pFailure->strCondition) ? pFailure->strCondition : ""); if (i != 1) { bctbx_free(buffer); } buffer = tmp; } } return buffer; } void write_suite_result_file(char *suite_name, char *results_string) { bctbx_vfs_file_t* bctbx_file; char *suite_name_wo_spaces, *file_name; suite_name_wo_spaces = bctbx_replace(bctbx_strdup(suite_name), ' ', '_'); file_name = bc_sprintf("%s.result", suite_name_wo_spaces); bctbx_file = bctbx_file_open(bctbx_vfs_get_default(), file_name, "w+"); if (bctbx_file) { bctbx_file_truncate(bctbx_file, 0); bctbx_file_fprintf(bctbx_file, 0, results_string); bctbx_file_close(bctbx_file); } bctbx_free(suite_name_wo_spaces); bctbx_free(file_name); } void merge_and_print_results_files(void) { bctbx_vfs_file_t* bctbx_file; int i; ssize_t file_size, read_bytes; char *buffer, *tmp; char *suite_name_wo_spaces, *file_name; char *results = NULL; for (i = 0; i < nb_test_suites; i++) { suite_name_wo_spaces = bctbx_replace(bctbx_strdup(test_suite[i]->name), ' ', '_'); file_name = bc_sprintf("%s.result", suite_name_wo_spaces); bctbx_file = bctbx_file_open2(bctbx_vfs_get_default(), file_name, O_RDONLY); if (bctbx_file) { file_size = (ssize_t) bctbx_file_size(bctbx_file); if (file_size > 0) { buffer = malloc(file_size + 1); read_bytes = bctbx_file_read(bctbx_file, (void *)buffer, file_size, 0); if (read_bytes == file_size) { buffer[read_bytes] = '\0'; if (results == NULL) { results = bctbx_concat("Suite '", test_suite[i]->name, "' results:\n", buffer, NULL); } else { tmp = bctbx_concat(results, "\nSuite '", test_suite[i]->name, "' results:\n", buffer, NULL); bctbx_free(results); results = tmp; } } else { bc_tester_printf(bc_printf_verbosity_error, "Failed to read suite results file '%s'", file_name); } bctbx_free(buffer); } else { bc_tester_printf(bc_printf_verbosity_error, "Empty suite results file '%s'", file_name); } remove(file_name); bctbx_file_close(bctbx_file); } else { bc_tester_printf(bc_printf_verbosity_error, "Failed to open suite results file '%s'", file_name); //Assume suite crash and report it. if (results == NULL) { results = bctbx_concat("Suite '", test_suite[i]->name, "' results: CRASH\n", NULL); } else { tmp = bctbx_concat(results, "\nSuite '", test_suite[i]->name, "' results: CRASH\n", NULL); bctbx_free(results); results = tmp; } } bctbx_free(suite_name_wo_spaces); bctbx_free(file_name); } if (results) { bc_tester_printf(bc_printf_verbosity_info, "Tests suites results: \n%s", results); bctbx_free(results); } } static void all_complete_message_handler(const CU_pFailureRecord pFailure) { #ifdef HAVE_CU_GET_SUITE if (run_in_parallel != 0) { if (suite_name) { char *results = CU_get_run_results_string(); write_suite_result_file(suite_name, results); CU_FREE(results); } else { merge_and_print_results_files(); } } else { char *results = CU_get_run_results_string(); bc_tester_printf(bc_printf_verbosity_info,"\n%s", results); CU_FREE(results); } #endif } static void suite_init_failure_message_handler(const CU_pSuite pSuite) { bc_tester_printf(bc_printf_verbosity_error,"Suite initialization failed for [%s]", pSuite->pName); } static void suite_cleanup_failure_message_handler(const CU_pSuite pSuite) { bc_tester_printf(bc_printf_verbosity_error,"Suite cleanup failed for [%s]", pSuite->pName); } #ifdef HAVE_CU_GET_SUITE static uint64_t suite_start_time = 0; static void suite_start_message_handler(const CU_pSuite pSuite) { bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] started\n", pSuite->pName); suite_start_time = bctbx_get_cur_time_ms(); bc_current_suite_name = pSuite->pName; } static void suite_complete_message_handler(const CU_pSuite pSuite, const CU_pFailureRecord pFailure) { bc_tester_printf(bc_printf_verbosity_info, "Suite [%s] ended in %.3f sec\n", pSuite->pName, (bctbx_get_cur_time_ms() - suite_start_time) / 1000.f); } static uint64_t test_start_time = 0; static void test_start_message_handler(const CU_pTest pTest, const CU_pSuite pSuite) { bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] Test [%s] started", pSuite->pName,pTest->pName); test_start_time = bctbx_get_cur_time_ms(); bc_current_test_name = pTest->pName; } /*derivated from bcunit*/ static void test_complete_message_handler(const CU_pTest pTest, const CU_pSuite pSuite, const CU_pFailureRecord pFailureList) { int i; CU_pFailureRecord pFailure = pFailureList; char *buffer = NULL; char* result = bc_sprintf("Suite [%s] Test [%s] %s in %.3f secs", pSuite->pName, pTest->pName, pFailure ? "failed" : "passed", (bctbx_get_cur_time_ms() - test_start_time) / 1000.f); if (pFailure) { for (i = 1; (NULL != pFailure); pFailure = pFailure->pNext, i++) { buffer = bc_sprintf("%s\n %d. %s:%u - %s", result, i, (NULL != pFailure->strFileName) ? pFailure->strFileName : "", pFailure->uiLineNumber, (NULL != pFailure->strCondition) ? pFailure->strCondition : ""); bctbx_free(result); result = buffer; } } bc_tester_printf(bc_printf_verbosity_info,"%s", result); bctbx_free(result); //insert empty line bc_tester_printf(bc_printf_verbosity_info,""); #ifdef __linux__ /* use mallinfo() to monitor allocated space. It is linux specific but other methods don't work: * setrlimit() RLIMIT_DATA doesn't count memory allocated via mmap() (which is used internally by malloc) * setrlimit() RLIMIT_AS works but also counts virtual memory allocated by thread stacks, which is very big and * hardly controllable. * setrlimit() RLIMIT_RSS does nothing interesting on linux. * getrusage() of RSS is unreliable: memory blocks can be leaked without being read or written, which would not * appear in RSS. * mallinfo() itself is the less worse solution. Allocated bytes are returned as 'int' so limited to 2GB */ if (max_vm_kb) { struct mallinfo minfo = mallinfo(); if ((size_t)minfo.uordblks > max_vm_kb * 1024) { bc_tester_printf( bc_printf_verbosity_error, "The program exceeded the maximum amount of memory allocatable (%i bytes), aborting now.\n", minfo.uordblks); abort(); } } #endif } #endif // char *get_logfile_name(const char *base_name, const char *suite_name) { if (suite_name) { char *name_wo_spaces = bctbx_replace(bctbx_strdup(suite_name), ' ', '_'); char *ret = bc_sprintf("%s_%s.log", base_name, name_wo_spaces); bctbx_free(name_wo_spaces); return ret; } else { return strdup(base_name); } } //Get the junit xml file name for a specific suite or the global one //If passed to BCUnit, it will append "-Results.xml" to the name //Use `suffix` to match the resulting name if needed char *get_junit_xml_file_name(const char *suite_name, const char *suffix) { char *xml_tmp_file; if (suite_name) { int suiteIdx = bc_tester_suite_index(suite_name); if (suffix) { xml_tmp_file = bc_sprintf("%s_%d%s", xml_file, suiteIdx, suffix); } else { xml_tmp_file = bc_sprintf("%s_%d", xml_file, suiteIdx); } } else { if (suffix) { xml_tmp_file = bc_sprintf("%s%s", xml_file, suffix); } else { xml_tmp_file = bc_sprintf("%s", xml_file); } } return xml_tmp_file; } //In case tests are started in parallel. //Merge partial JUnit suites reports into the final XML file void merge_junit_xml_files(const char *dst_file_name) { char **suite_junit_xml_results; char *file_name; bctbx_vfs_file_t* bctbx_file; ssize_t read_bytes = 0, file_size = 0, offset = 0; int i; suite_junit_xml_results = malloc(sizeof(char *) * nb_test_suites); for (i = 0; i < nb_test_suites; i++) { file_name = get_junit_xml_file_name(test_suite[i]->name, "-Results.xml"); bctbx_file = bctbx_file_open2(bctbx_vfs_get_default(), file_name, O_RDONLY); if (bctbx_file != NULL) { file_size = (ssize_t)bctbx_file_size(bctbx_file); suite_junit_xml_results[i] = malloc(file_size + 1); read_bytes = bctbx_file_read(bctbx_file, (void *)suite_junit_xml_results[i], file_size, 0); if (read_bytes == file_size) { suite_junit_xml_results[i][file_size] = '\0'; //Also remove the file bctbx_file_close(bctbx_file); remove(file_name); } else { bc_tester_printf(bc_printf_verbosity_error, "Could not read JUnit XML file '%s' to merge", file_name); bctbx_free(suite_junit_xml_results[i]); suite_junit_xml_results[i] = NULL; bctbx_file_close(bctbx_file); } } else { bc_tester_printf(bc_printf_verbosity_error, "Could not open JUnit XML file '%s' to merge", file_name); suite_junit_xml_results[i] = NULL; bctbx_file_close(bctbx_file); } bctbx_free(file_name); } //Empty the destination file bctbx_file = bctbx_file_open(bctbx_vfs_get_default(), dst_file_name, "w+"); if(bctbx_file){ bctbx_file_truncate(bctbx_file, 0); offset = bctbx_file_fprintf(bctbx_file, 0, "\n\n"); for (i = 0; i < nb_test_suites; i++) { if (suite_junit_xml_results[i] != NULL) { offset += bctbx_file_fprintf(bctbx_file, offset, suite_junit_xml_results[i]); bctbx_free(suite_junit_xml_results[i]); } } bctbx_file_fprintf(bctbx_file, offset, "\n"); bctbx_file_close(bctbx_file); } bctbx_free(suite_junit_xml_results); } //In case tests are started in parallel AND --log-file was given //Merge individual suite log file into a global one void merge_log_files(const char *base_logfile_name) { bctbx_vfs_file_t* dst_file; bctbx_vfs_file_t* bctbx_file; void *buf; ssize_t offset = 0, file_size = 0, read_bytes = 0; int i; dst_file = bctbx_file_open(bctbx_vfs_get_default(), base_logfile_name, "w+"); if (!dst_file) { bc_tester_printf(bc_printf_verbosity_error, "Failed to create target log file '%s'", base_logfile_name); return; } for (i = 0; i < nb_test_suites; ++i) { char *suite_logfile_name = get_logfile_name(log_file_name, test_suite[i]->name); bctbx_file = bctbx_file_open2(bctbx_vfs_get_default(), suite_logfile_name, O_RDONLY); if (!bctbx_file) { bc_tester_printf(bc_printf_verbosity_error, "Could not open log file '%s' to merge into '%s'", suite_logfile_name, base_logfile_name); continue; } file_size = (ssize_t)bctbx_file_size(bctbx_file); buf = malloc(file_size); read_bytes = bctbx_file_read(bctbx_file, buf, file_size, 0); if (read_bytes == file_size) { offset += bctbx_file_write(dst_file, buf, file_size, offset); } else { bc_tester_printf(bc_printf_verbosity_error, "Could not read log file '%s' to merge into '%s'", suite_logfile_name, base_logfile_name); } bctbx_file_close(bctbx_file); bctbx_free(suite_logfile_name); bctbx_free(buf); } bctbx_file_close(dst_file); } //Number of test suites to run concurrently int bc_tester_get_max_parallel_processes(void) { if (max_parallel_suites == 0) return (nb_test_suites / 2) + 1; return MIN(max_parallel_suites, nb_test_suites); } void bc_tester_set_max_parallel_suites(int nb_suites){ max_parallel_suites = nb_suites; } void bc_tester_set_max_failed_tests_threshold(int threshold){ max_failed_tests_threshold = threshold; } void bc_tester_set_global_timeout(int seconds){ globalTimeout = seconds; } #ifdef _WIN32 void kill_sub_processes(int *pids) { // TODO: Windows support } #else //If there was an error, kill zombies void kill_sub_processes(int *pids) { int i; for (i = 0; i < nb_test_suites; ++i) { if (pids[i] > 0) { kill(pids[i], SIGTERM); } } } #endif #ifdef _WIN32 //Start test subprocess for the given suite int start_sub_process(const char *suite_name, PROCESS_INFORMATION * pi) { int argc = 0; int i; //const char *argv[origin_argc + 10]; //Assume safey 10 more parameters char * commandLine = bctbx_strdup(origin_argv[0]); char * commandLineTemp = NULL; bool_t propagateXmlFileSetter = TRUE; bool_t propagateLogFileSetter = log_file_name!=NULL;// Log file has not been set : Don't propagate it for (i = 1; origin_argv[i]; ++i) { if (strcmp(origin_argv[i], "--xml-file") == 0) { propagateXmlFileSetter = FALSE; } if (strcmp(origin_argv[i], "--log-file") == 0) { //Create a specific log file for this suite commandLineTemp = commandLine; commandLine = bc_sprintf("%s %s \"%s\"", commandLineTemp, origin_argv[i], get_logfile_name(log_file_name, suite_name) ); bctbx_free(commandLineTemp); propagateLogFileSetter = FALSE; ++i; } else { //Keep other parameters commandLineTemp = commandLine; commandLine = bc_sprintf("%s %s", commandLineTemp, origin_argv[i] ); bctbx_free(commandLineTemp); } } commandLineTemp = commandLine; commandLine = bc_sprintf("%s --xml --child --suite \"%s\"", commandLineTemp, suite_name); bctbx_free(commandLineTemp); if( propagateXmlFileSetter){// Add current xml_file to child commands commandLineTemp = commandLine; commandLine = bc_sprintf("%s --xml-file \"%s\"", commandLineTemp, xml_file); bctbx_free(commandLineTemp); } if(propagateLogFileSetter){ commandLineTemp = commandLine; commandLine = bc_sprintf("%s --log-file \"%s\"", commandLineTemp, get_logfile_name(log_file_name, suite_name) ); bctbx_free(commandLineTemp); } // Start the child process. STARTUPINFOA si; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( pi, sizeof(*pi) ); if( !CreateProcessA( NULL, // No module name (use command line) commandLine, // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0,//CREATE_NEW_CONSOLE, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure pi ) // Pointer to PROCESS_INFORMATION structure ) { printf( "CreateProcess failed (%d).\n", GetLastError() ); return GetLastError(); } bctbx_free(commandLine); return 0; } #else //Start test subprocess for the given suite int start_sub_process(const char *suite_name) { int argc = 0; int i; const char *argv[origin_argc + 10]; //Assume safey 10 more parameters argv[argc++] = origin_argv[0]; for (i = 1; origin_argv[i]; ++i) { if (strcmp(origin_argv[i], "--verbose") == 0) { argv[argc++] = origin_argv[i]; } else if (strcmp(origin_argv[i], "--silent") == 0) { argv[argc++] = origin_argv[i]; } else if (strcmp(origin_argv[i], "--log-file") == 0) { //Create a specific log file for this suite argv[argc++] = origin_argv[i++]; argv[argc++] = get_logfile_name(log_file_name, suite_name); } else if (strcmp(origin_argv[i], "--xml-file") == 0) { argv[argc++] = origin_argv[i++]; argv[argc++] = origin_argv[i]; } else if (strcmp(origin_argv[i], "--parallel") == 0) { argv[argc++] = origin_argv[i]; } else { //Keep unknown parameters argv[argc++] = origin_argv[i]; } } argv[argc++] = "--xml"; argv[argc++] = "--suite"; argv[argc++] = suite_name; argv[argc] = NULL; return execv(argv[0], (char **) argv); } #endif //For parallel tests only - handle anormally exited test suites //Remove previously generated XML suite file if exited anormally (could cause unusable final JUnit XML) //And mark all tests for the suite as failed int handle_sub_process_error(int pid, int *suitesPids) { int failed_tests = 0; int i, j; for (i = 0; i < nb_test_suites; ++i) { if (suitesPids[i] == pid) { ssize_t offset; char *suite_file_name = get_junit_xml_file_name(test_suite[i]->name, "-Results.xml"); bctbx_vfs_file_t* bctbx_file = bctbx_file_open(bctbx_vfs_get_default(), suite_file_name, "w+"); bctbx_file_truncate(bctbx_file, 0); offset = bctbx_file_fprintf(bctbx_file, 0, "\n\n", test_suite[i]->name, test_suite[i]->nb_tests, test_suite[i]->nb_tests); failed_tests = test_suite[i]->nb_tests; for (j=0; j < test_suite[i]->nb_tests; ++j) { offset += bctbx_file_fprintf(bctbx_file, offset, "\t\n", test_suite[i]->name, test_suite[i]->tests[j].name); offset += bctbx_file_fprintf(bctbx_file, offset, "\t\t\n\t\tGlobal suite failure\n"); offset += bctbx_file_fprintf(bctbx_file, offset, "\t\t\n\t\n"); } bctbx_file_fprintf(bctbx_file, offset, "\n\n"); bc_tester_printf(bc_printf_verbosity_info, "Suite '%s' ended in error. Marking all tests as failed", test_suite[i]->name); bctbx_file_close(bctbx_file); bctbx_free(suite_file_name); break; } } return failed_tests; } #ifdef _WIN32 int bc_tester_run_parallel(void) { int ret = 0; //Global return status; if( nb_test_suites > 0){ PROCESS_INFORMATION * suitesPids = malloc(nb_test_suites * sizeof( PROCESS_INFORMATION)); int *processed = malloc(nb_test_suites * sizeof(int)); uint64_t time_start = bctbx_get_cur_time_ms(), elapsed = time_start, print_timer = time_start; //Assume there is a problem if a suite is still running 60mn after the start of the tester. TODO make timeout a cli parameter ? uint64_t timeout = 0; if (globalTimeout <= 0) { globalTimeout = 60 * 60; } timeout = time_start + (globalTimeout * 1000); int maxProcess = bc_tester_get_max_parallel_processes(); int nextSuite = 0; //Next suite id to be exec'd int runningSuites = 0; //Number of currently running suites int testsFinished = 0; memset(suitesPids, 0, nb_test_suites*sizeof(PROCESS_INFORMATION)); memset(processed, 0, nb_test_suites*sizeof(int)); do { if (nextSuite < nb_test_suites && runningSuites < maxProcess) { PROCESS_INFORMATION pi; if (start_sub_process(test_suite[nextSuite]->name, &pi) != 0) { bc_tester_printf(bc_printf_verbosity_error, "Error while starting suite sub-process. Aborting."); return -1; } suitesPids[nextSuite] = pi; runningSuites++; nextSuite++; }else{ for(int i = 0 ; i < nextSuite ; ++i){ if( processed[i] == 0){ int returnCode = WaitForSingleObject( suitesPids[i].hProcess, 1000 ); if( ( returnCode != WAIT_TIMEOUT)){ --runningSuites; ++testsFinished; bc_tester_printf(bc_printf_verbosity_error, "Suite sub process (ID %d) terminated with return code %d.", i, returnCode); processed[i] = 1; ret += returnCode; } } } } bctbx_sleep_ms(50); if (elapsed - print_timer > 10000) { //print message only every ~10s... bc_tester_printf(bc_printf_verbosity_error, "Waiting for test suites to finish... Total Suites(%d). Suites running(%d), Finished(%d)", nb_test_suites, runningSuites, testsFinished); print_timer = bctbx_get_cur_time_ms(); } elapsed = bctbx_get_cur_time_ms(); } while (testsFinished < nb_test_suites); // This code has been commented out in order to display failing test of timed out suites in the allure report // } while (testsFinished < nb_test_suites && elapsed < timeout); if (elapsed >= timeout) { bc_tester_printf(bc_printf_verbosity_error, "Stopped waiting for all test suites to execute as we reach timeout. Killing running suites."); // This code has been commented out in order to display failing test of timed out suites in the allure report /* for(int i = 0 ; i < nextSuite ; ++i){ if( processed[i] == 0) TerminateProcess(suitesPids[i].hProcess, -1); } */ bc_tester_printf(bc_printf_verbosity_error, "*** Test suite took too much time. Please check errors or split longest test suites to benefit from parallel execution. ***"); ret = -1; } for(int i = 0 ; i < nextSuite ; ++i){ CloseHandle( suitesPids[i].hProcess ); CloseHandle( suitesPids[i].hThread ); } bc_tester_printf(bc_printf_verbosity_info, "All suites ended."); if (ret != -1){ int seconds = (int)(elapsed - time_start)/1000; all_complete_message_handler(NULL); bc_tester_printf(bc_printf_verbosity_info, "Full parallel run completed in %2i mn %2i s.\n", seconds/60, seconds % 60); } free( suitesPids); free( processed); } return ret == -1 ? ret : (ret > max_failed_tests_threshold); } #else int bc_tester_run_parallel(void) { int suitesPids[nb_test_suites]; uint64_t time_start = bctbx_get_cur_time_ms(), elapsed = time_start, print_timer = time_start; uint64_t timeout = 0; if (globalTimeout <= 0) { globalTimeout = 60 * 60; } timeout = time_start + (globalTimeout * 1000); int maxProcess = bc_tester_get_max_parallel_processes(); int nextSuite = 0; //Next suite id to be exec'd int runningSuites = 0; //Number of currently running suites int testsFinished = 0; int ret = 0; //Global return status; memset(suitesPids, 0, sizeof(suitesPids)); do { if (nextSuite < nb_test_suites && runningSuites < maxProcess) { int pid = fork(); if (pid == -1) { bc_tester_printf(bc_printf_verbosity_error, "Error during fork() while starting child process. Aborting."); return -1; } else if (pid > 0) { suitesPids[nextSuite] = pid; runningSuites++; nextSuite++; } else { if (start_sub_process(test_suite[nextSuite]->name) == -1) { bc_tester_printf(bc_printf_verbosity_error, "Error while starting suite sub-process. Aborting."); return -1; } } } if (runningSuites > 0) { int wstatus, childPid, childRet; if ((childPid = waitpid(-1, &wstatus, WNOHANG)) == -1) { bc_tester_printf(bc_printf_verbosity_error, "Error during waitpid() while waiting for child process. Aborting."); return -1; } if (childPid != 0) { if (WIFEXITED(wstatus) || WIFSIGNALED(wstatus)) { --runningSuites; ++testsFinished; } if (WIFSIGNALED(wstatus)) { childRet = handle_sub_process_error(childPid, suitesPids); } else { childRet = WEXITSTATUS(wstatus); } ret += childRet; bc_tester_printf(bc_printf_verbosity_error, "Suite sub process (pid %d) terminated with return code %d.", childPid, childRet); } } bctbx_sleep_ms(50); if (elapsed - print_timer > 10000) { //print message only every ~10s... bc_tester_printf(bc_printf_verbosity_error, "Waiting for test suites to finish... Total Suites(%d). Suites running(%d), Finished(%d)", nb_test_suites, runningSuites, testsFinished); print_timer = bctbx_get_cur_time_ms(); } elapsed = bctbx_get_cur_time_ms(); } while (testsFinished < nb_test_suites); // This code has been commented out in order to display failing test of timed out suites in the allure report // } while (testsFinished < nb_test_suites && elapsed < timeout); if (elapsed >= timeout) { bc_tester_printf(bc_printf_verbosity_error, "Stopped waiting for all test suites to execute as we reach timeout. Killing running suites."); bc_tester_printf(bc_printf_verbosity_error, "*** Test suite took too much time. Please check errors or split longest test suites to benefit from parallel execution. ***"); // This code has been commented out in order to display failing test of timed out suites in the allure report //kill_sub_processes(suitesPids); ret = -1; } bc_tester_printf(bc_printf_verbosity_info, "All suites ended."); if (ret != -1){ int seconds = (int)(elapsed - time_start)/1000; all_complete_message_handler(NULL); bc_tester_printf(bc_printf_verbosity_info, "Full parallel run completed in %2i mn %2i s.\n", seconds/60, seconds % 60); return ret > max_failed_tests_threshold; } return -1; } #endif int bc_tester_run_tests(const char *suite_name, const char *test_name, const char *tag_name) { int ret = 0; if ((ret = bc_tester_register_suites()) != 0) { return ret; } #ifdef HAVE_CU_GET_SUITE CU_set_suite_start_handler(suite_start_message_handler); CU_set_suite_complete_handler(suite_complete_message_handler); CU_set_test_start_handler(test_start_message_handler); CU_set_test_complete_handler(test_complete_message_handler); #endif CU_set_all_test_complete_handler(all_complete_message_handler); CU_set_suite_init_failure_handler(suite_init_failure_message_handler); CU_set_suite_cleanup_failure_handler(suite_cleanup_failure_message_handler); if (xml_enabled == 1 && !test_name) { char *xml_file_name;//, *xml_file_name_tmp; CU_automated_enable_junit_xml(TRUE); /* this requires 3.0.1 because previous versions crash automated.c */ if (run_in_parallel != 0) { if (suite_name) { //Sub-process started by parent in bc_tester_run_parallel CU_automated_enable_partial_junit(TRUE); xml_file_name = get_junit_xml_file_name(suite_name, NULL); CU_set_output_filename(xml_file_name); bctbx_free(xml_file_name); CU_automated_run_tests(); } else { //Starting registered suites in parallel ret = bc_tester_run_parallel(); if (ret != -1){ /* -1 means timeout, in this case don't generate junit report. */ xml_file_name = get_junit_xml_file_name(NULL, "-Results.xml"); merge_junit_xml_files(xml_file_name); bctbx_free(xml_file_name); if (log_file_name) { merge_log_files(log_file_name); } } return ret; } } else { //Classic, non-parallel run xml_file_name = get_junit_xml_file_name(NULL, NULL); CU_set_output_filename(xml_file_name); CU_automated_run_tests(); bctbx_free(xml_file_name); } goto end; } #ifndef HAVE_CU_GET_SUITE if (suite_name) { bc_tester_printf(bc_printf_verbosity_info, "Tester compiled without CU_get_suite() function, running all tests instead of suite '%s'", suite_name); } #else if (suite_name) { CU_pSuite suite; suite = CU_get_suite(suite_name); if (!suite) { if (tag_name != NULL) { bc_tester_printf(bc_printf_verbosity_error, "Could not find suite '%s' or this suite has no tests with tag '%s'. Available suites are:", suite_name, tag_name); } else { bc_tester_printf(bc_printf_verbosity_error, "Could not find suite '%s'. Available suites are:", suite_name); } bc_tester_list_suites(); return -1; } else if (test_name) { CU_pTest test=CU_get_test_by_name(test_name, suite); if (!test) { if (tag_name != NULL) { bc_tester_printf(bc_printf_verbosity_error, "Could not find test '%s' in suite '%s' or this test is not tagged '%s'. Available tests are:", test_name, suite_name, tag_name); } else { bc_tester_printf(bc_printf_verbosity_error, "Could not find test '%s' in suite '%s'. Available tests are:", test_name, suite_name); } // do not use suite_name here, since this method is case sensitive bc_tester_list_tests(suite->pName); return -2; } else { CU_ErrorCode err= CU_run_test(suite, test); if (err != CUE_SUCCESS) bc_tester_printf(bc_printf_verbosity_error, "CU_basic_run_test error %d", err); } } else { CU_run_suite(suite); } } else #endif { #ifdef HAVE_CU_CURSES if (curses) { /* Run tests using the BCUnit curses interface */ CU_curses_run_tests(); } else #endif { /* Run all tests using the BCUnit Basic interface */ CU_run_all_tests(); } } end: #ifdef __linux__ bc_tester_printf(bc_printf_verbosity_info, "Still %i kilobytes allocated when all tests are finished.", mallinfo().uordblks / 1024); #endif if (run_in_parallel){ // We are a child process, return the number of test failed. int failed_tests = CU_get_number_of_tests_failed(); bc_tester_printf(bc_printf_verbosity_info, "Suite %s finished with %i failed tests.", suite_name, failed_tests); if (failed_tests >= 255) { bc_tester_printf(bc_printf_verbosity_error, "The number of tests exceeded 255, the maximum value for an exit status !"); failed_tests = 255; } return failed_tests; } /* Otherwise it is serialized execution.*/ return (int)CU_get_number_of_tests_failed() > (int)max_failed_tests_threshold; } #if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(__ANDROID__) && !defined(IOS) static int file_exists(const char* root_path) { char * res_path = bc_sprintf("%s/%s", root_path, expected_res); int err = bctbx_file_exist(res_path); bctbx_free(res_path); return err == 0; } #endif static void detect_res_prefix(const char* prog) { char* progpath = NULL; char* progname = NULL; FILE* writable_file = NULL; if (prog != NULL) { char *ptr; progpath = strdup(prog); if (strchr(prog, '/') != NULL) { progpath[strrchr(prog, '/') - prog + 1] = '\0'; } else if (strchr(prog, '\\') != NULL) { progpath[strrchr(prog, '\\') - prog + 1] = '\0'; } ptr = strrchr(prog, '/'); if (ptr == NULL) { ptr = strrchr(prog, '\\'); } if (ptr != NULL) { #ifdef BC_TESTER_WINDOWS_DESKTOP char *exe = strstr(prog, ".exe"); if (exe != NULL) exe[0] = '\0'; #endif progname = strdup(ptr + 1); } } #if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(__ANDROID__) && !defined(IOS) { char* prefix = NULL; char *installed_resources_path = NULL; if ((progname != NULL) && (progpath != NULL)) { installed_resources_path = bc_sprintf("%s../share/%s", progpath, progname); } if (file_exists(".")) { prefix = strdup("."); } else if (file_exists("..")) { prefix = strdup(".."); } else if ((installed_resources_path != NULL) && file_exists(installed_resources_path)) { prefix = strdup(installed_resources_path); } else if (progpath) { //for autotools, binary is in .libs/ subdirectory char * progpath2 = bc_sprintf("%s/../", progpath); if (file_exists(progpath)) { prefix = strdup(progpath); } else if (file_exists(progpath2)) { prefix = strdup(progpath2); } bctbx_free(progpath2); } if (installed_resources_path != NULL) bctbx_free(installed_resources_path); if (bc_tester_resource_dir_prefix != NULL && !file_exists(bc_tester_resource_dir_prefix)) { bc_tester_printf(bc_printf_verbosity_error, "Invalid provided resource directory: could not find expected resources '%s' in '%s'.", expected_res, bc_tester_resource_dir_prefix); bctbx_free(bc_tester_resource_dir_prefix); bc_tester_resource_dir_prefix = NULL; } if (prefix != NULL) { if (bc_tester_resource_dir_prefix == NULL) { bc_tester_printf(bc_printf_verbosity_error, "Resource directory set to %s", prefix); bc_tester_set_resource_dir_prefix(prefix); } if (bc_tester_writable_dir_prefix == NULL) { bc_tester_printf(bc_printf_verbosity_error, "Writable directory set to %s", prefix); bc_tester_set_writable_dir_prefix(prefix); } bctbx_free(prefix); } } #endif // check that we can write in writable directory if (bc_tester_writable_dir_prefix != NULL) { char * writable_file_path = bc_sprintf("%s/%s", bc_tester_writable_dir_prefix, ".bc_tester_utils.tmp"); writable_file = fopen(writable_file_path, "w"); if (writable_file) { fclose(writable_file); } bctbx_free(writable_file_path); } if (bc_tester_resource_dir_prefix == NULL || writable_file == NULL) { if (bc_tester_resource_dir_prefix == NULL) { bc_tester_printf(bc_printf_verbosity_error, "Could not find resource directory '%s' in '%s'! Please try again using option --resource-dir.", expected_res, progpath); } if (writable_file == NULL) { bc_tester_printf(bc_printf_verbosity_error, "Failed to write file in %s. Please try again using option --writable-dir.", bc_tester_writable_dir_prefix); } abort(); } if (progpath != NULL) { bctbx_free(progpath); } if(progname) { bctbx_free(progname); } } //Default function for the `--verbose`cli option int bc_tester_verbose_handler(const char *arg) { bctbx_set_log_level(BCTBX_LOG_DOMAIN, BCTBX_LOG_DEBUG); return 0; } //Default function for the `--silent` cli option int bc_tester_silent_handler(const char *arg) { bctbx_set_log_level(BCTBX_LOG_DOMAIN, BCTBX_LOG_FATAL); return 0; } //Default function for the `--log-file`cli option int bc_tester_logfile_handler(const char *arg) { int res = 0; char *dir = bctbx_dirname(arg); char *base = bctbx_basename(arg); 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: bctbx_free(dir); bctbx_free(base); return res; } void bc_tester_init(void (*ftester_printf)(int level, const char *format, va_list args), int iverbosity_info, int iverbosity_error, const char* aexpected_res) { //Set default cli arguments handlers for --silent, --verbose, --log-file if undefined if (silent_arg_func == NULL) { silent_arg_func = bc_tester_silent_handler; } if (verbose_arg_func == NULL) { verbose_arg_func = bc_tester_verbose_handler; } if (logfile_arg_func == NULL) { logfile_arg_func = bc_tester_logfile_handler; } tester_printf_va = ftester_printf; bc_printf_verbosity_error = iverbosity_error; bc_printf_verbosity_info = iverbosity_info; if (!bc_tester_writable_dir_prefix) { bc_tester_writable_dir_prefix = strdup("."); } if (aexpected_res) { expected_res = strdup(aexpected_res); } } void bc_tester_set_max_vm(size_t amax_vm_kb) { #ifdef __linux__ max_vm_kb = (size_t)amax_vm_kb; bc_tester_printf(bc_printf_verbosity_info, "Maximum virtual memory space set to %li kilo bytes", max_vm_kb); #else bc_tester_printf(bc_printf_verbosity_error, "Maximum virtual memory space setting is only implemented on Linux."); #endif } void bc_tester_helper(const char *name, const char* additionnal_helper) { bc_tester_printf(bc_printf_verbosity_info, "%s --help\n" #ifdef HAVE_CU_CURSES "\t\t\t--curses\n" #endif "\t\t\t--verbose\n" "\t\t\t--silent\n" "\t\t\t--log-file \n" "\t\t\t--list-suites\n" "\t\t\t--list-tests \n" "\t\t\t--suite \n" "\t\t\t--test \n" "\t\t\t--tag (execute all tests with the given tag)\n" "\t\t\t--all (execute all tests, even the ones with the Skip flag)\n" "\t\t\t--resource-dir (directory where tester resource are located)\n" "\t\t\t--writable-dir (directory where temporary files should be created)\n" "\t\t\t--xml\n" "\t\t\t--xml-file \n" "\t\t\t--max-alloc (maximum amount of memory obtained via malloc allocator)\n" "\t\t\t--max-alloc (maximum amount of memory obtained via malloc allocator)\n" "\t\t\t--parallel (Execute tests concurrently and with JUnit report)\n" "\t\t\t--parallel-max (Number Max of parallel processes)\n" "\t\t\t--timeout (sets the global timeout when used alongside to the parallel option, the default value is 60)\n" "And additionally:\n" "%s", name, additionnal_helper); } int bc_tester_parse_args(int argc, char **argv, int argid) { int ret = 0; int i = argid; if (strcmp(argv[i],"--help")==0){ return -1; } else if (strcmp(argv[i],"--log-file")==0) { CHECK_ARG("--log-file", ++i, argc); ret = logfile_arg_func(argv[i]); if (ret < 0) return ret; log_file_name = argv[i]; } else if (strcmp(argv[i],"--silent")==0) { ret = silent_arg_func(argv[i]); if (ret < 0) return ret; } else if (strcmp(argv[i],"--verbose")==0) { ret = verbose_arg_func(argv[i]); if (ret < 0) return ret; } else if (strcmp(argv[i],"--test")==0){ CHECK_ARG("--test", ++i, argc); test_name=argv[i]; } else if (strcmp(argv[i],"--suite")==0){ CHECK_ARG("--suite", ++i, argc); suite_name=argv[i]; } else if (strcmp(argv[i], "--tag") == 0) { CHECK_ARG("--tag", ++i, argc); tag_name = argv[i]; } else if (strcmp(argv[i], "--all") == 0) { run_skipped_tests = 1; } else if (strcmp(argv[i],"--list-suites")==0){ bc_tester_list_suites(); return 0; } else if (strcmp(argv[i],"--list-tests")==0){ CHECK_ARG("--list-tests", ++i, argc); suite_name = argv[i]; bc_tester_list_tests(suite_name); return 0; } else if (strcmp(argv[i], "--xml-file") == 0) { CHECK_ARG("--xml-file", ++i, argc); xml_file = argv[i]; xml_enabled = 1; } else if (strcmp(argv[i], "--xml") == 0) { xml_enabled = 1; } else if (strcmp(argv[i], "--parallel") == 0) { //Keep record of cli args for subprocesses if (!origin_argv) { origin_argv = argv; origin_argc = argc; } //Defaults to JUnit report if parallel is enabled xml_enabled = 1; run_in_parallel = 1; } else if (strcmp(argv[i], "--parallel-max") == 0) { CHECK_ARG("--parallel-max", ++i, argc); bc_tester_set_max_parallel_suites(atoi(argv[i])); } else if (strcmp(argv[i], "--timeout") == 0) { CHECK_ARG("--timeout", ++i, argc); globalTimeout = atoi(argv[i]) * 60; } else if (strcmp(argv[i], "--max-alloc") == 0) { CHECK_ARG("--max-alloc", ++i, argc); max_vm_kb = atol(argv[i]); } else if (strcmp(argv[i], "--resource-dir") == 0) { CHECK_ARG("--resource-dir", ++i, argc); if (bc_tester_resource_dir_prefix) bctbx_free(bc_tester_resource_dir_prefix); bc_tester_resource_dir_prefix = strdup(argv[i]); } else if (strcmp(argv[i], "--writable-dir") == 0) { CHECK_ARG("--writable-dir", ++i, argc); if (bc_tester_writable_dir_prefix) bctbx_free(bc_tester_writable_dir_prefix); bc_tester_writable_dir_prefix = strdup(argv[i]); }else if(strcmp(argv[i],"--child")==0){//Switch off this parameter as it is used for external processing } else { bc_tester_printf(bc_printf_verbosity_error, "Unknown option \"%s\"", argv[i]); return -1; } /* returns number of arguments read + 1 */ return i - argid + 1; } //Init BCUnit and register suites and/or tests into internal BCUnit registry before actual BCUnit test launch int bc_tester_register_suites(void) { //Assume everything is already setup if BCUnit registry exists if (CU_registry_initialized()) { return 0; } if (CUE_SUCCESS != CU_initialize_registry()) return CU_get_error(); if (suite_name != NULL) { int suiteIdx = bc_tester_suite_index(suite_name); if (suiteIdx == -1) { bc_tester_printf(bc_printf_verbosity_error, "Suite with name \"%s\" not found. Available suites are: ", suite_name); bc_tester_list_suites(); return -1; } bc_tester_register_suite(test_suite[suiteIdx], tag_name); } else { int i; for (i = 0; i < nb_test_suites; i++) { bc_tester_register_suite(test_suite[i], tag_name); } } return 0; } int bc_tester_start(const char* prog_name) { int ret; if (expected_res) detect_res_prefix(prog_name); if (max_vm_kb) bc_tester_set_max_vm(max_vm_kb); ret = bc_tester_run_tests(suite_name, test_name, tag_name); return ret; } void bc_tester_add_suite(test_suite_t *suite) { if (test_suite == NULL) { test_suite = (test_suite_t **)malloc(10 * sizeof(test_suite_t *)); } test_suite[nb_test_suites] = suite; nb_test_suites++; if ((nb_test_suites % 10) == 0) { test_suite = (test_suite_t **)realloc(test_suite, (nb_test_suites + 10) * sizeof(test_suite_t *)); } } void bc_tester_uninit(void) { /* Redisplay list of failed tests on end */ /*BUG: do not display list of failures on mingw, it crashes mysteriously*/ #if !defined(_WIN32) && !defined(_MSC_VER) /* Redisplay list of failed tests on end */ if (CU_get_number_of_failure_records()){ CU_basic_show_failures(CU_get_failure_list()); } #endif CU_cleanup_registry(); /*add missing final newline*/ bc_tester_printf(bc_printf_verbosity_info,""); if (test_suite != NULL) { bctbx_free(test_suite); test_suite = NULL; nb_test_suites = 0; } if (bc_tester_resource_dir_prefix != NULL) { bctbx_free(bc_tester_resource_dir_prefix); bc_tester_resource_dir_prefix = NULL; } if (bc_tester_writable_dir_prefix != NULL) { bctbx_free(bc_tester_writable_dir_prefix); bc_tester_writable_dir_prefix = NULL; } } static void bc_tester_set_dir_prefix(char **prefix, const char *name) { if (*prefix != NULL) bctbx_free(*prefix); *prefix = strdup(name); } const char * bc_tester_get_resource_dir_prefix(void) { return bc_tester_resource_dir_prefix; } void bc_tester_set_resource_dir_prefix(const char *name) { bc_tester_set_dir_prefix(&bc_tester_resource_dir_prefix, name); } const char * bc_tester_get_writable_dir_prefix(void) { return bc_tester_writable_dir_prefix; } void bc_tester_set_writable_dir_prefix(const char *name) { bc_tester_set_dir_prefix(&bc_tester_writable_dir_prefix, name); } static char * bc_tester_path(const char *prefix, const char *name) { if (name) { return bc_sprintf("%s/%s", prefix, name); } else { return NULL; } } char * bc_tester_res(const char *name) { return bc_tester_path(bc_tester_resource_dir_prefix, name); } char * bc_tester_file(const char *name) { return bc_tester_path(bc_tester_writable_dir_prefix, name); } char* bc_sprintfva(const char* format, va_list args) { /* Guess we need no more than 100 bytes. */ int n, size = 200; char *p,*np; #ifndef _WIN32 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/ #endif if ((p = malloc(size)) == NULL) return NULL; while (1) { /* Try to print in the allocated space. */ #ifndef _WIN32 va_copy(cap,args); n = vsnprintf (p, size, format, cap); va_end(cap); #else /*this works on 32 bits, luckily*/ n = vsnprintf (p, size, format, args); #endif /* If that worked, return the string. */ if (n > -1 && n < size) return p; //bc_tester_printf(bc_printf_verbosity_error, "Reallocing space."); /* Else try again with more space. */ if (n > -1) /* glibc 2.1 */ size = n + 1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ if ((np = realloc (p, size)) == NULL) { bctbx_free(p); return NULL; } else { p = np; } } } char* bc_sprintf(const char* format, ...) { va_list args; char* res; va_start(args, format); res = bc_sprintfva(format, args); va_end (args); return res; } void bc_free(void *ptr) { free(ptr); } const char * bc_tester_current_suite_name(void) { return bc_current_suite_name; } const char * bc_tester_current_test_name(void) { return bc_current_test_name; } const char ** bc_tester_current_test_tags(void) { if (bc_current_suite_name && bc_current_test_name) { int suite_index = bc_tester_suite_index(bc_current_suite_name); int test_index = bc_tester_test_index(test_suite[suite_index], bc_current_test_name); return test_suite[suite_index]->tests[test_index].tags; } return NULL; } unsigned int bc_get_number_of_failures(void) { return CU_get_number_of_failures(); } void bc_set_trace_handler(void(*handler)(int, const char*, va_list)) { #ifdef HAVE_CU_SET_TRACE_HANDLER CU_set_trace_handler(handler); #else bc_tester_printf(bc_printf_verbosity_error, "CU_set_trace_handler not implemented"); #endif } int bc_assert(const char* file, int line, int predicate, const char* format) { if (!predicate) bc_tester_printf(bc_printf_verbosity_info, format, NULL); return CU_assertImplementation(predicate, line, format, file, "", FALSE); } bctoolbox-5.2.0/src/utils.h000066400000000000000000000014141434566643100156250ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox/port.h"bctoolbox-5.2.0/src/utils/000077500000000000000000000000001434566643100154545ustar00rootroot00000000000000bctoolbox-5.2.0/src/utils/exception.cc000066400000000000000000000062031434566643100177620ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif // ifdef HAVE_CONFIG_H #ifdef HAVE_EXECINFO #include #include #include #include #include #endif #include "bctoolbox/exception.hh" #include "bctoolbox/logging.h" using namespace std; #ifdef HAVE_EXECINFO static void uncaught_handler() { std::exception_ptr p = current_exception(); try { rethrow_exception(p); } catch (BctbxException &e) { BCTBX_SLOGE << e; } catch (std::exception &ee) { BCTBX_SLOGE << "Unexpected exception [" << ee.what() << " ] use BctbxException for better debug"; } abort(); } #endif BctbxException::BctbxException(const std::string &message) : mSize(0) { #ifdef HAVE_EXECINFO mSize = backtrace(mArray, sizeof(mArray) / sizeof(void *)); #else mSize = 0; #endif if (!message.empty()) mOs << message; #ifdef HAVE_EXECINFO #if __clang if (get_terminate() != uncaught_handler) #endif set_terminate(uncaught_handler); // invoke in case of uncautch exception for this thread #endif } BctbxException::BctbxException(const BctbxException &other) : mSize(other.mSize) { memcpy(mArray, other.mArray, sizeof(mArray)); mOs << other.str(); } void BctbxException::printStackTrace() const { #ifdef HAVE_EXECINFO backtrace_symbols_fd(mArray + 1, mSize - 1, STDERR_FILENO); #else std::cerr << "stack trace not available on this platform" << std::endl; #endif } void BctbxException::printStackTrace(std::ostream &os) const { #ifdef HAVE_EXECINFO char **bt = backtrace_symbols(mArray, mSize); int position=0; for (unsigned int i = 1; i < mSize; ++i) { Dl_info info; char *demangled = NULL; int status = -1; if (dladdr(mArray[i], &info) && info.dli_sname) { demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status); os << position++ << setw(20) << basename((char*)info.dli_fname) << setw(16) << info.dli_saddr ; os << " "; if (demangled) { os << demangled; free(demangled); } else{ os << info.dli_sname; } } else { os << bt[i]; } os << std::endl; } free(bt); #else os << "stack trace not available on this platform"; #endif } const char *BctbxException::what() const noexcept { return str().c_str(); } const std::string& BctbxException::str() const { mMessage = mOs.str(); return mMessage; } std::ostream &operator<<(std::ostream &__os, const BctbxException &e) { __os << e.str() << std::endl; e.printStackTrace(__os); return __os; } bctoolbox-5.2.0/src/utils/ios_utils.mm000066400000000000000000000072521434566643100200270ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 . */ #ifdef __APPLE__ #include #import #include #include "bctoolbox/ios_utils.hh" #include "bctoolbox/exception.hh" #include "bctoolbox/logging.h" #include "ios_utils_stub.hh" #if TARGET_OS_IPHONE #import #endif namespace bctoolbox { std::unique_ptr IOSUtils::sInstance = nullptr; IOSUtils& IOSUtils::getUtils() { if (!sInstance) { sInstance = std::unique_ptr(new IOSUtils); } return *sInstance; } IOSUtils::IOSUtils() { mIsApp = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".app"]; if (mIsApp) { openDynamicLib(); using create_t = IOSUtilsInterface *(*)(); auto createUtils = reinterpret_cast(loadSymbol("bctbx_create_ios_utils_app")); mUtils = createUtils(); } else { mUtils = new IOSUtilsStub(); } } IOSUtils::~IOSUtils() { if (mIsApp) { using destroy_t = void (*)(IOSUtilsInterface *); auto destroyUtils = reinterpret_cast(loadSymbol("bctbx_destroy_ios_utils_app")); destroyUtils(mUtils); dlclose(mHandle); } else { delete mUtils; } } bool IOSUtils::isApp() { return mIsApp; } void IOSUtils::openDynamicLib() { NSString *frameworkPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString: @"/Frameworks/bctoolbox-ios.framework/bctoolbox-ios"]; const char *frameworkChar = [frameworkPath cStringUsingEncoding:[NSString defaultCStringEncoding]]; mHandle = dlopen(frameworkChar, RTLD_LAZY); if (!mHandle) { throw BCTBX_EXCEPTION << "bctoolbox error dlopen : " << dlerror(); } // reset errors dlerror(); } void *IOSUtils::loadSymbol(const char *symbol) { void *loadedSymbol = dlsym(mHandle, symbol); const char *dlsym_error = dlerror(); if (dlsym_error) { std::string error = dlsym_error; dlclose(mHandle); throw BCTBX_EXCEPTION << "bctoolbox error dlsym : " << error; } return loadedSymbol; } unsigned long IOSUtils::beginBackgroundTask(const char *name, std::function cb) { return mUtils->beginBackgroundTask(name, cb); } void IOSUtils::endBackgroundTask(unsigned long id) { return mUtils->endBackgroundTask(id); } bool IOSUtils::isApplicationStateActive() { return mUtils->isApplicationStateActive(); } int IOSUtils::getOSMajorVersion() const{ #if TARGET_OS_IPHONE NSArray *versionCompatibility = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."]; return [[versionCompatibility objectAtIndex:0] intValue]; #else bctbx_error("IOSUtils::getOSMajorVersion() not running on iOS"); return 0; #endif } unsigned long IOSUtilsStub::beginBackgroundTask(const char *name, std::function cb) { return 0; } void IOSUtilsStub::endBackgroundTask(unsigned long id) {} bool IOSUtilsStub::isApplicationStateActive() { return false; } } //namespace bctoolbox #endif bctoolbox-5.2.0/src/utils/ios_utils_app.hh000066400000000000000000000021171434566643100206500ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 . */ #pragma once #include "bctoolbox/ios_utils.hh" namespace bctoolbox { class IOSUtilsApp : public IOSUtilsInterface { public: unsigned long beginBackgroundTask(const char *name, std::function cb) override; void endBackgroundTask(unsigned long id) override; bool isApplicationStateActive() override; }; } //namespace bctoolbox bctoolbox-5.2.0/src/utils/ios_utils_app.mm000066400000000000000000000075241434566643100206710ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 #if TARGET_OS_IPHONE #include #include #include "ios_utils_app.hh" #include "bctoolbox/logging.h" namespace bctoolbox { unsigned long IOSUtilsApp::beginBackgroundTask(const char *name, std::function cb) { __block UIBackgroundTaskIdentifier bgid = UIBackgroundTaskInvalid; dispatch_block_t block = ^{ UIApplication *app=[UIApplication sharedApplication]; @try { if (cb==nullptr){ bctbx_error("belle_sip_begin_background_task(): the callback must not be NULL. Application must be aware that the background task needs to be terminated."); bgid = UIBackgroundTaskInvalid; @throw([NSException exceptionWithName:@"LinphoneCoreException" reason:@"Background task has no callback" userInfo:nil]); } void (^handler)() = ^{ cb(); }; if([app respondsToSelector:@selector(beginBackgroundTaskWithName:expirationHandler:)]){ bgid = [app beginBackgroundTaskWithName:[NSString stringWithUTF8String:name] expirationHandler:handler]; } else { bgid = [app beginBackgroundTaskWithExpirationHandler:handler]; } if (bgid==UIBackgroundTaskInvalid){ bctbx_error("Could not start background task %s.", name); bgid = 0; @throw([NSException exceptionWithName:@"LinphoneCoreException" reason:@"Could not start background task" userInfo:nil]); } // backgroundTimeRemaining is properly set only when running background... but not immediately! if (app.applicationState != UIApplicationStateBackground || (app.backgroundTimeRemaining == DBL_MAX)) { bctbx_message("Background task %s started. Unknown remaining time since application is not fully in background.", name); } else { bctbx_message("Background task %s started. Remaining time %.1f secs", name, app.backgroundTimeRemaining); } } @catch (NSException*) { // do nothing } }; if( [NSThread isMainThread] ) { block(); } else { dispatch_sync(dispatch_get_main_queue(), block); } return (unsigned long)bgid; } void IOSUtilsApp::endBackgroundTask(unsigned long id) { dispatch_block_t block = ^{ UIApplication *app=[UIApplication sharedApplication]; if (id != UIBackgroundTaskInvalid){ [app endBackgroundTask:(UIBackgroundTaskIdentifier)id]; } }; if( [NSThread isMainThread] ) { block(); } else { dispatch_sync(dispatch_get_main_queue(), block); } } bool IOSUtilsApp::isApplicationStateActive() { return ([UIApplication sharedApplication].applicationState == UIApplicationStateActive); } } //namespace bctoolbox extern "C" { bctoolbox::IOSUtilsInterface *bctbx_create_ios_utils_app() { return new bctoolbox::IOSUtilsApp; } void bctbx_destroy_ios_utils_app(bctoolbox::IOSUtilsInterface* p) { delete p; } } #endif bctoolbox-5.2.0/src/utils/ios_utils_stub.hh000066400000000000000000000021201434566643100210370ustar00rootroot00000000000000/* * Copyright (c) 2010-2019 Belledonne Communications SARL. * * This file is part of Liblinphone. * * 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 . */ #pragma once #include "bctoolbox/ios_utils.hh" namespace bctoolbox { class IOSUtilsStub : public IOSUtilsInterface { public: unsigned long beginBackgroundTask(const char *name, std::function cb) override; void endBackgroundTask(unsigned long id) override; bool isApplicationStateActive() override; }; } //namespace bctoolbox bctoolbox-5.2.0/src/utils/port.c000066400000000000000000001567401434566643100166210ustar00rootroot00000000000000 /* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/logging.h" #include "bctoolbox/port.h" #include "bctoolbox/vconnect.h" #include "bctoolbox/list.h" #include "bctoolbox/charconv.h" #include "utils.h" #ifdef __APPLE__ #include "TargetConditionals.h" #endif #if defined(_WIN32) && !defined(_WIN32_WCE) #include #include #endif #ifdef _MSC_VER #ifndef access #define access _access #endif #endif #ifndef _WIN32 #include /* available on POSIX system only */ #else #include #endif #ifdef HAVE_SYS_SHM_H #include #endif #ifdef __linux__ #include #endif #ifndef MIN #define MIN(a,b) a<=b ? a : b #endif static void *bctbx_libc_malloc(size_t sz){ return malloc(sz); } static void *bctbx_libc_realloc(void *ptr, size_t sz){ return realloc(ptr,sz); } static void bctbx_libc_free(void*ptr){ free(ptr); } static bool_t allocator_used=FALSE; static BctoolboxMemoryFunctions bctbx_allocator={ bctbx_libc_malloc, bctbx_libc_realloc, bctbx_libc_free }; void bctbx_set_memory_functions(BctoolboxMemoryFunctions *functions){ if (allocator_used){ bctbx_fatal("bctbx_set_memory_functions() must be called before " "first use of bctbx_malloc or bctbx_realloc"); return; } bctbx_allocator=*functions; } void* bctbx_malloc(size_t sz){ allocator_used=TRUE; return bctbx_allocator.malloc_fun(sz); } void* bctbx_realloc(void *ptr, size_t sz){ allocator_used=TRUE; return bctbx_allocator.realloc_fun(ptr,sz); } void bctbx_free(void* ptr){ bctbx_allocator.free_fun(ptr); } void * bctbx_malloc0(size_t size){ void *ptr=bctbx_malloc(size); memset(ptr,0,size); return ptr; } char * bctbx_strdup(const char *tmp){ size_t sz; char *ret; if (tmp==NULL) return NULL; sz=strlen(tmp)+1; ret=(char*)bctbx_malloc(sz); strcpy(ret,tmp); ret[sz-1]='\0'; return ret; } char * bctbx_dirname(const char *path) { char *ptr = strrchr(path, '/'); if (ptr == NULL) ptr = strrchr(path, '\\'); return ptr ? bctbx_strndup(path, (int) (ptr-path)) : bctbx_strdup("."); } char * bctbx_basename(const char *path) { char *ptr = strrchr(path, '/'); if (ptr == NULL) ptr = strrchr(path, '\\'); if (ptr == NULL) return bctbx_strdup(path); return bctbx_strdup(ptr + 1); } /* * this method is an utility method that calls fnctl() on UNIX or * ioctlsocket on Win32. * int retrun the result of the system method */ int bctbx_socket_set_non_blocking(bctbx_socket_t sock){ #if !defined(_WIN32) && !defined(_WIN32_WCE) return fcntl (sock, F_SETFL, O_NONBLOCK); #else unsigned long nonBlock = 1; return ioctlsocket(sock, FIONBIO , &nonBlock); #endif } int bctbx_file_exist(const char *pathname) { return access(pathname,F_OK); } bool_t bctbx_directory_exists(const char *pathname) { struct stat sb; #ifdef WIN32 return stat(pathname, &sb) == 0 && (_S_IFDIR & sb.st_mode); #else return stat(pathname, &sb) == 0 && S_ISDIR(sb.st_mode); #endif } bctbx_list_t *bctbx_parse_directory(const char *path, const char *file_type) { bctbx_list_t* file_list = NULL; #ifdef _WIN32 WIN32_FIND_DATA FileData; HANDLE hSearch; BOOL fFinished = FALSE; char szDirPath[1024]; #ifdef UNICODE wchar_t wszDirPath[1024]; #endif if (file_type == NULL) { file_type = ".*"; } snprintf(szDirPath, sizeof(szDirPath), "%s\\*%s", path, file_type); #ifdef UNICODE mbstowcs(wszDirPath, szDirPath, sizeof(wszDirPath)); hSearch = FindFirstFileExW(wszDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0); #else hSearch = FindFirstFileExA(szDirPath, FindExInfoStandard, &FileData, FindExSearchNameMatch, NULL, 0); #endif if (hSearch == INVALID_HANDLE_VALUE) { bctbx_message("No file (*%s) found in [%s] [%d].", file_type, szDirPath, (int)GetLastError()); return NULL; } snprintf(szDirPath, sizeof(szDirPath), "%s", path); while (!fFinished) { char szFilePath[1024]; // ignore . and .. if (!(FileData.cFileName[0] == '.' && ( (FileData.cFileName[1] == '\0') || (FileData.cFileName[1] == '.' && FileData.cFileName[2] == '\0')))) { #ifdef UNICODE char filename[512]; wcstombs(filename, FileData.cFileName, sizeof(filename)); snprintf(szFilePath, sizeof(szFilePath), "%s\\%s", szDirPath, filename); #else snprintf(szFilePath, sizeof(szFilePath), "%s\\%s", szDirPath, FileData.cFileName); #endif file_list = bctbx_list_append(file_list, bctbx_strdup(szFilePath)); } if (!FindNextFile(hSearch, &FileData)) { if (GetLastError() == ERROR_NO_MORE_FILES) { fFinished = TRUE; } else { bctbx_error("Couldn't find next (*%s) file.", file_type); fFinished = TRUE; } } } /* Close the search handle. */ FindClose(hSearch); #else DIR *dir; struct dirent *ent; if ((dir = opendir(path)) == NULL) { bctbx_error("Could't open [%s] directory.", path); return NULL; } /* loop on all directory files */ errno = 0; ent = readdir(dir); while (ent != NULL) { /* filter on file type if given */ if (file_type==NULL || (strncmp(ent->d_name+strlen(ent->d_name)-strlen(file_type), file_type, strlen(file_type))==0) ) { /* ignore . and .. */ if (!(ent->d_name[0] == '.' && ((ent->d_name[1]=='.' && ent->d_name[2]=='\0') || ent->d_name[1]=='\0'))) { char *name_with_path=bctbx_strdup_printf("%s/%s",path,ent->d_name); file_list = bctbx_list_append(file_list, name_with_path); } } ent = readdir(dir); } if (errno != 0) { bctbx_error("Error while reading the [%s] directory: %s.", path, strerror(errno)); } closedir(dir); #endif return file_list; } int bctbx_mkdir(const char *path) { #ifdef _WIN32 return _mkdir(path); #else return mkdir(path, 0700); #endif }; /** * delete empty directory only * * @param[in] path the directory to delete * * @return 0 on success */ static int bctbx_rmemptydir(const char *path) { #ifdef _WIN32 return _rmdir(path); #else return rmdir(path); #endif }; /** * Callback used to delete files from parsed list in directory * @param[in] v_path the path of the file or dir to delete, it shall never be . or .. * it should be the case as the list was obtained using bctbx_parse_directory * */ static void bctbx_removeFileFromList(void *v_path) { char *path = (char *)v_path; // if path is a directory, recurse in it if (bctbx_directory_exists(path)) { bctbx_rmdir(path, TRUE); } else { remove(path); } } int bctbx_rmdir(const char *path, bool_t recursive) { if (recursive == FALSE) { return bctbx_rmemptydir(path); } // check the directory exists if (!bctbx_directory_exists(path)) { return -1; } // get all files from directory and delete them bctbx_list_t *fileList = bctbx_parse_directory(path, NULL); bctbx_list_for_each(fileList, bctbx_removeFileFromList); bctbx_list_free_with_data(fileList, bctbx_free); // directory is now empty, delete it return bctbx_rmemptydir(path); } #if !defined(_WIN32) && !defined(_WIN32_WCE) /* Use UNIX inet_aton method */ #else int __bctbx_WIN_inet_aton (const char * cp, struct in_addr * addr) { unsigned long retval; retval = inet_addr (cp); if (retval == INADDR_NONE) { return -1; } else { addr->S_un.S_addr = retval; return 1; } } #endif char *bctbx_strndup(const char *str,int n){ int min=MIN((int)strlen(str),n)+1; char *ret=(char*)bctbx_malloc(min); strncpy(ret,str,min); ret[min-1]='\0'; return ret; } #if !defined(_WIN32) && !defined(_WIN32_WCE) int __bctbx_thread_join(bctbx_thread_t thread, void **ptr){ int err=pthread_join(thread,ptr); if (err!=0) { bctbx_error("pthread_join error: %s",strerror(err)); } return err; } int __bctbx_thread_create(bctbx_thread_t *thread, pthread_attr_t *attr, void * (*routine)(void*), void *arg){ pthread_attr_t my_attr; pthread_attr_init(&my_attr); if (attr) my_attr = *attr; #ifdef BCTBX_DEFAULT_THREAD_STACK_SIZE if (BCTBX_DEFAULT_THREAD_STACK_SIZE!=0) pthread_attr_setstacksize(&my_attr, BCTBX_DEFAULT_THREAD_STACK_SIZE); #endif return pthread_create(thread, &my_attr, routine, arg); } unsigned long __bctbx_thread_self(void) { return (unsigned long)pthread_self(); } void bctbx_set_self_thread_name(const char *name){ #ifdef __linux__ /* Android, Gnu/Linux */ prctl(PR_SET_NAME, name, NULL, NULL, NULL); #elif TARGET_OS_MAC pthread_setname_np(name); #elif bctbx_warning("bctbx_set_self_thread_name(): not implemented on this platform."); #endif } #endif #if defined(_WIN32) || defined(_WIN32_WCE) int __bctbx_WIN_mutex_init(bctbx_mutex_t *mutex, void *attr) { #ifdef BCTBX_WINDOWS_DESKTOP *mutex=CreateMutex(NULL, FALSE, NULL); #else InitializeSRWLock(mutex); #endif return 0; } int __bctbx_WIN_mutex_lock(bctbx_mutex_t * hMutex) { #ifdef BCTBX_WINDOWS_DESKTOP WaitForSingleObject(*hMutex, INFINITE); /* == WAIT_TIMEOUT; */ #else AcquireSRWLockExclusive(hMutex); #endif return 0; } int __bctbx_WIN_mutex_unlock(bctbx_mutex_t * hMutex) { #ifdef BCTBX_WINDOWS_DESKTOP ReleaseMutex(*hMutex); #else ReleaseSRWLockExclusive(hMutex); #endif return 0; } int __bctbx_WIN_mutex_destroy(bctbx_mutex_t * hMutex) { #ifdef BCTBX_WINDOWS_DESKTOP CloseHandle(*hMutex); #endif return 0; } void bctbx_set_self_thread_name(const char *name){ wchar_t *unicode_name = bctbx_string_to_wide_string(name); SetThreadDescription(GetCurrentThread(), unicode_name); bctbx_free(unicode_name); } typedef struct thread_param{ void * (*func)(void *); void * arg; }thread_param_t; static unsigned WINAPI thread_starter(void *data){ thread_param_t *params=(thread_param_t*)data; params->func(params->arg); bctbx_free(data); return 0; } #if defined _WIN32_WCE # define _beginthreadex CreateThread # define _endthreadex ExitThread #endif int __bctbx_WIN_thread_create(bctbx_thread_t *th, void *attr, void * (*func)(void *), void *data) { thread_param_t *params=bctbx_new(thread_param_t,1); params->func=func; params->arg=data; *th=(HANDLE)_beginthreadex( NULL, 0, thread_starter, params, 0, NULL); return 0; } int __bctbx_WIN_thread_join(bctbx_thread_t thread_h, void **unused) { if (thread_h!=NULL) { WaitForSingleObjectEx(thread_h, INFINITE, FALSE); CloseHandle(thread_h); } return 0; } unsigned long __bctbx_WIN_thread_self(void) { return (unsigned long)GetCurrentThreadId(); } int __bctbx_WIN_cond_init(bctbx_cond_t *cond, void *attr) { #ifdef BCTBX_WINDOWS_DESKTOP *cond=CreateEvent(NULL, FALSE, FALSE, NULL); #else InitializeConditionVariable(cond); #endif return 0; } int __bctbx_WIN_cond_wait(bctbx_cond_t* hCond, bctbx_mutex_t * hMutex) { #ifdef BCTBX_WINDOWS_DESKTOP //gulp: this is not very atomic ! bug here ? __bctbx_WIN_mutex_unlock(hMutex); WaitForSingleObject(*hCond, INFINITE); __bctbx_WIN_mutex_lock(hMutex); #else SleepConditionVariableSRW(hCond, hMutex, INFINITE, 0); #endif return 0; } int __bctbx_WIN_cond_signal(bctbx_cond_t * hCond) { #ifdef BCTBX_WINDOWS_DESKTOP SetEvent(*hCond); #else WakeConditionVariable(hCond); #endif return 0; } int __bctbx_WIN_cond_broadcast(bctbx_cond_t * hCond) { __bctbx_WIN_cond_signal(hCond); return 0; } int __bctbx_WIN_cond_destroy(bctbx_cond_t * hCond) { #ifdef BCTBX_WINDOWS_DESKTOP CloseHandle(*hCond); #endif return 0; } #if defined(_WIN32_WCE) #include const char * bctbx_strerror(DWORD value) { static TCHAR msgBuf[256]; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, value, 0, // Default language (LPTSTR) &msgBuf, 0, NULL ); return (const char *)msgBuf; } int gettimeofday (struct timeval *tv, void *tz) { DWORD timemillis = GetTickCount(); tv->tv_sec = timemillis/1000; tv->tv_usec = (timemillis - (tv->tv_sec*1000)) * 1000; return 0; } #else int bctbx_gettimeofday (struct timeval *tv, void* tz) { union { __int64 ns100; /*time since 1 Jan 1601 in 100ns units */ FILETIME fileTime; } now; GetSystemTimeAsFileTime (&now.fileTime); tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); return (0); } #endif const char *__bctbx_getWinSocketError(int error) { static char buf[256]; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL); return buf; } #ifdef _WORKAROUND_MINGW32_BUGS char * WSAAPI gai_strerror(int errnum){ return (char*)__bctbx_getWinSocketError(errnum); } #endif #endif #ifndef _WIN32 #include #include #include #include static char *make_pipe_name(const char *name){ return bctbx_strdup_printf("/tmp/%s",name); } /* portable named pipes */ bctbx_socket_t bctbx_server_pipe_create_by_path(const char *path){ struct sockaddr_un sa; char *pipename=bctbx_strdup(path); bctbx_socket_t sock; sock=socket(AF_UNIX,SOCK_STREAM,0); sa.sun_family=AF_UNIX; strncpy(sa.sun_path,pipename,sizeof(sa.sun_path)-1); unlink(pipename);/*in case we didn't finished properly previous time */ bctbx_free(pipename); fchmod(sock,S_IRUSR|S_IWUSR); if (bind(sock,(struct sockaddr*)&sa,sizeof(sa))!=0){ bctbx_error("Failed to bind command unix socket: %s",strerror(errno)); return -1; } listen(sock,1); return sock; } bctbx_socket_t bctbx_server_pipe_create(const char *name){ return bctbx_server_pipe_create_by_path(make_pipe_name(name)); } bctbx_socket_t bctbx_server_pipe_accept_client(bctbx_socket_t server){ struct sockaddr_un su; socklen_t ssize=sizeof(su); bctbx_socket_t client_sock=accept(server,(struct sockaddr*)&su,&ssize); return client_sock; } int bctbx_server_pipe_close_client(bctbx_socket_t client){ return close(client); } int bctbx_server_pipe_close(bctbx_socket_t spipe){ struct sockaddr_un sa; socklen_t len=sizeof(sa); int err; /*this is to retrieve the name of the pipe, in order to unlink the file*/ err=getsockname(spipe,(struct sockaddr*)&sa,&len); if (err==0){ unlink(sa.sun_path); }else bctbx_error("getsockname(): %s",strerror(errno)); return close(spipe); } bctbx_socket_t bctbx_client_pipe_connect(const char *name){ bctbx_socket_t sock = -1; struct sockaddr_un sa; struct stat fstats; char *pipename=make_pipe_name(name); uid_t uid = getuid(); // check that the creator of the pipe is us if( (stat(name, &fstats) == 0) && (fstats.st_uid != uid) ){ bctbx_error("UID of file %s (%lu) differs from ours (%lu)", pipename, (unsigned long)fstats.st_uid, (unsigned long)uid); return -1; } sock = socket(AF_UNIX,SOCK_STREAM,0); sa.sun_family=AF_UNIX; strncpy(sa.sun_path,pipename,sizeof(sa.sun_path)-1); bctbx_free(pipename); if (connect(sock,(struct sockaddr*)&sa,sizeof(sa))!=0){ close(sock); return -1; } return sock; } int bctbx_pipe_read(bctbx_socket_t p, uint8_t *buf, int len){ return read(p,buf,len); } int bctbx_pipe_write(bctbx_socket_t p, const uint8_t *buf, int len){ return write(p,buf,len); } int bctbx_client_pipe_close(bctbx_socket_t sock){ return close(sock); } #ifdef HAVE_SYS_SHM_H void *bctbx_shm_open(unsigned int keyid, int size, int create){ key_t key=keyid; void *mem; int perms=S_IRUSR|S_IWUSR; int fd=shmget(key,size,create ? (IPC_CREAT | perms ) : perms); if (fd==-1){ printf("shmget failed: %s\n",strerror(errno)); return NULL; } mem=shmat(fd,NULL,0); if (mem==(void*)-1){ printf("shmat() failed: %s", strerror(errno)); return NULL; } return mem; } void bctbx_shm_close(void *mem){ shmdt(mem); } #endif #elif defined(_WIN32) && !defined(_WIN32_WCE) static char *make_pipe_name(const char *name){ return bctbx_strdup_printf("\\\\.\\pipe\\%s",name); } static HANDLE event=NULL; bctbx_pipe_t bctbx_server_pipe_create_by_path(const char *path){ #ifdef BCTBX_WINDOWS_DESKTOP bctbx_pipe_t h; char *pipename=bctbx_strdup(path); #ifdef BCTBX_WINDOWS_UWP wchar_t * wPipename = bctbx_string_to_wide_string(pipename); h=CreateNamedPipe(wPipename,PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_MESSAGE|PIPE_WAIT,1, 32768,32768,0,NULL); bctbx_free(wPipename); #else h=CreateNamedPipe(pipename,PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_MESSAGE|PIPE_WAIT,1, 32768,32768,0,NULL); #endif bctbx_free(pipename); if (h==INVALID_HANDLE_VALUE){ bctbx_error("Fail to create named pipe %s",pipename); } if (event==NULL) event=CreateEvent(NULL,TRUE,FALSE,NULL); return h; #else bctbx_error("%s not supported!", __FUNCTION__); return INVALID_HANDLE_VALUE; #endif } bctbx_pipe_t bctbx_server_pipe_create(const char *name){ return bctbx_server_pipe_create_by_path(make_pipe_name(name)); } /*this function is a bit complex because we need to wakeup someday even if nobody connects to the pipe. bctbx_server_pipe_close() makes this function to exit. */ bctbx_pipe_t bctbx_server_pipe_accept_client(bctbx_pipe_t server){ #ifdef BCTBX_WINDOWS_DESKTOP OVERLAPPED ol; DWORD undef; HANDLE handles[2]; memset(&ol,0,sizeof(ol)); ol.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); ConnectNamedPipe(server,&ol); handles[0]=ol.hEvent; handles[1]=event; WaitForMultipleObjects(2,handles,FALSE,INFINITE); if (GetOverlappedResult(server,&ol,&undef,FALSE)){ CloseHandle(ol.hEvent); return server; } CloseHandle(ol.hEvent); return INVALID_HANDLE_VALUE; #else bctbx_error("%s not supported!", __FUNCTION__); return INVALID_HANDLE_VALUE; #endif } int bctbx_server_pipe_close_client(bctbx_pipe_t server){ #ifdef BCTBX_WINDOWS_DESKTOP return DisconnectNamedPipe(server)==TRUE ? 0 : -1; #else bctbx_error("%s not supported!", __FUNCTION__); return -1; #endif } int bctbx_server_pipe_close(bctbx_pipe_t spipe){ #ifdef BCTBX_WINDOWS_DESKTOP SetEvent(event); //CancelIoEx(spipe,NULL); /*vista only*/ return CloseHandle(spipe); #else bctbx_error("%s not supported!", __FUNCTION__); return -1; #endif } bctbx_pipe_t bctbx_client_pipe_connect(const char *name){ #if defined(BCTBX_WINDOWS_DESKTOP) && !defined(BCTBX_WINDOWS_UWP) char *pipename=make_pipe_name(name); bctbx_pipe_t hpipe = CreateFile( pipename, // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // default security attributes OPEN_EXISTING, // opens existing pipe 0, // default attributes NULL); // no template file bctbx_free(pipename); return hpipe; #else bctbx_error("%s not supported!", __FUNCTION__); return INVALID_HANDLE_VALUE; #endif } int bctbx_pipe_read(bctbx_pipe_t p, uint8_t *buf, int len){ DWORD ret=0; if (ReadFile(p,buf,len,&ret,NULL)) return ret; /*bctbx_error("Could not read from pipe: %s",strerror(GetLastError()));*/ return -1; } int bctbx_pipe_write(bctbx_pipe_t p, const uint8_t *buf, int len){ DWORD ret=0; if (WriteFile(p,buf,len,&ret,NULL)) return ret; /*bctbx_error("Could not write to pipe: %s",strerror(GetLastError()));*/ return -1; } int bctbx_client_pipe_close(bctbx_pipe_t sock){ return CloseHandle(sock); } typedef struct MapInfo{ HANDLE h; void *mem; }MapInfo; static bctbx_list_t *maplist=NULL; void *bctbx_shm_open(unsigned int keyid, int size, int create){ #ifdef BCTBX_WINDOWS_DESKTOP HANDLE h; void *buf; #ifdef BCTBX_WINDOWS_UWP char nameBuf[64]; snprintf(nameBuf,sizeof(nameBuf),"%x",keyid); wchar_t * name = bctbx_string_to_wide_string(nameBuf); #else char name[64]; snprintf(name,sizeof(name),"%x",keyid); #endif if (create){ h = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE, // read/write access 0, // maximum object size (high-order DWORD) size, // maximum object size (low-order DWORD) name); // name of mapping object }else{ h = OpenFileMapping( FILE_MAP_ALL_ACCESS, // read/write access FALSE, // do not inherit the name name); // name of mapping object } if (h==(HANDLE)-1) { bctbx_error("Fail to open file mapping (create=%i)",create); return NULL; } buf = (LPTSTR) MapViewOfFile(h, // handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, size); if (buf!=NULL){ MapInfo *i=(MapInfo*)bctbx_new(MapInfo,1); i->h=h; i->mem=buf; maplist=bctbx_list_append(maplist,i); }else{ CloseHandle(h); bctbx_error("MapViewOfFile failed"); } #ifdef BCTBX_WINDOWS_UWP bctbx_free(name); #endif return buf; #else bctbx_error("%s not supported!", __FUNCTION__); return NULL; #endif } void bctbx_shm_close(void *mem){ #ifdef BCTBX_WINDOWS_DESKTOP bctbx_list_t *elem; for(elem=maplist;elem;elem=bctbx_list_next(elem)){ MapInfo *i=(MapInfo*)bctbx_list_get_data(elem); if (i->mem==mem){ CloseHandle(i->h); UnmapViewOfFile(mem); bctbx_free(i); maplist=bctbx_list_erase_link(maplist,elem); return; } } bctbx_error("No shared memory at %p was found.",mem); #else bctbx_error("%s not supported!", __FUNCTION__); #endif } #endif #ifdef __MACH__ #include #include #endif void _bctbx_get_cur_time(bctoolboxTimeSpec *ret, bool_t realtime){ #if defined(_WIN32_WCE) || defined(WIN32) #if defined(BCTBX_WINDOWS_DESKTOP) && !defined(ENABLE_MICROSOFT_STORE_APP) && !defined(BCTBX_WINDOWS_UWP) DWORD timemillis; # if defined(_WIN32_WCE) timemillis=GetTickCount(); # else timemillis=timeGetTime(); # endif ret->tv_sec=timemillis/1000; ret->tv_nsec=(timemillis%1000)*1000000LL; #else ULONGLONG timemillis = GetTickCount64(); ret->tv_sec = timemillis / 1000; ret->tv_nsec = (timemillis % 1000) * 1000000LL; #endif #elif defined(__MACH__) && defined(__GNUC__) && (__GNUC__ >= 3) struct timeval tv; gettimeofday(&tv, NULL); ret->tv_sec=tv.tv_sec; ret->tv_nsec=tv.tv_usec*1000LL; #elif defined(__MACH__) struct timeb time_val; ftime (&time_val); ret->tv_sec = time_val.time; ret->tv_nsec = time_val.millitm * 1000000LL; #else struct timespec ts; if (clock_gettime(realtime ? CLOCK_REALTIME : CLOCK_MONOTONIC,&ts)<0){ bctbx_fatal("clock_gettime() doesn't work: %s",strerror(errno)); } ret->tv_sec=ts.tv_sec; ret->tv_nsec=ts.tv_nsec; #endif } void bctbx_get_utc_cur_time(bctoolboxTimeSpec *ret){ _bctbx_get_cur_time(ret, TRUE); } void bctbx_get_cur_time(bctoolboxTimeSpec *ret){ _bctbx_get_cur_time(ret, FALSE); } uint64_t bctbx_get_cur_time_ms(void) { bctoolboxTimeSpec ts; _bctbx_get_cur_time(&ts, TRUE); return (ts.tv_sec * 1000LL) + ((ts.tv_nsec + 500000LL) / 1000000LL); } void bctbx_sleep_ms(int ms){ #ifdef _WIN32 #ifdef BCTBX_WINDOWS_DESKTOP Sleep(ms); #else HANDLE sleepEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS); if (!sleepEvent) return; WaitForSingleObjectEx(sleepEvent, ms, FALSE); CloseHandle(sleepEvent); #endif #else struct timespec ts; ts.tv_sec=ms/1000; ts.tv_nsec=(ms%1000)*1000000LL; nanosleep(&ts,NULL); #endif } void bctbx_sleep_until(const bctoolboxTimeSpec *ts){ #ifdef __linux__ struct timespec rq; rq.tv_sec=ts->tv_sec; rq.tv_nsec=ts->tv_nsec; while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &rq, NULL)==-1 && errno==EINTR){ } #else bctoolboxTimeSpec current; bctoolboxTimeSpec diff; _bctbx_get_cur_time(¤t, TRUE); diff.tv_sec=ts->tv_sec-current.tv_sec; diff.tv_nsec=ts->tv_nsec-current.tv_nsec; if (diff.tv_nsec<0){ diff.tv_nsec+=1000000000LL; diff.tv_sec-=1; } #ifdef _WIN32 bctbx_sleep_ms((int)((diff.tv_sec * 1000LL) + (diff.tv_nsec/1000000LL))); #else { struct timespec dur,rem; dur.tv_sec=diff.tv_sec; dur.tv_nsec=diff.tv_nsec; while (nanosleep(&dur,&rem)==-1 && errno==EINTR){ dur=rem; }; } #endif #endif } /** * @brief Add given amount of seconds to a timeSpec structure * * @param[in/out] ts The timeSpec structure used as input, modified in output by increnting it according to second argument * @param[in] lap In seconds, number of seconds to modify the given timeSpec, can be negative(which may set the original timeSpec to 0) */ void bctbx_timespec_add(bctoolboxTimeSpec *ts, const int64_t lap) { if (lap<0 && -lap > ts->tv_sec) { ts->tv_sec = 0; ts->tv_nsec = 0; } else { ts->tv_sec += lap; } } /** * @brief Compares two TimeSpec s1 and s2. * * @param[in] s1 First time spec * @param[in] s2 Second time spec * * @return a negative value if s1 is earlier than s2, 0 if they are equal, a positive value if s1 is later than s2 */ int bctbx_timespec_compare(const bctoolboxTimeSpec *s1, const bctoolboxTimeSpec *s2){ int64_t secdiff = s1->tv_sec - s2->tv_sec; if (secdiff == 0){ int64_t nsec_diff = s1->tv_nsec - s2->tv_nsec; if (nsec_diff < 0){ return -1; }else if (nsec_diff > 0){ return 1; }else return 0; }else if (secdiff < 0){ return -1; }else return 1; } uint32_t bctbx_time_string_to_sec(const char *timeString) { char *p = NULL; char *o = NULL; int32_t n=0; uint32_t ret=0; if (timeString == NULL) { return 0; } o = p = bctbx_strdup(timeString); while (*p!='\0') { n=strtol(p, &p, 10); switch (*p) { case '\0': ret+=n; break; case 'Y': ret +=n*365*24*3600; p++; break; case 'M': ret +=n*30*24*3600; p++; break; case 'W': ret +=n*7*24*3600; p++; break; case 'd': ret +=n*24*3600; p++; break; case 'h': ret +=n*3600; p++; break; case 'm': ret +=n*60; p++; break; case 's': ret+=n; p++; break; default: /* just ignore any other suffix */ p++; break; } } bctbx_free(o); return ret; } #if defined(_WIN32) && !defined(_MSC_VER) char* strtok_r(char *str, const char *delim, char **nextp){ char *ret; if (str == NULL){ str = *nextp; } str += strspn(str, delim); if (*str == '\0'){ return NULL; } ret = str; str += strcspn(str, delim); if (*str){ *str++ = '\0'; } *nextp = str; return ret; } #endif #if defined(_WIN32) && !defined(_MSC_VER) #include static int bctbx_wincrypto_random(unsigned int *rand_number){ static HCRYPTPROV hProv=(HCRYPTPROV)-1; static int initd=0; if (!initd){ if (!CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ bctbx_error("bctbx_wincrypto_random(): Could not acquire a windows crypto context"); return -1; } initd=TRUE; } if (hProv==(HCRYPTPROV)-1) return -1; if (!CryptGenRandom(hProv,4,(BYTE*)rand_number)){ bctbx_error("bctbx_wincrypto_random(): CryptGenRandom() failed."); return -1; } return 0; } #endif unsigned int bctbx_random(void){ #if defined(__linux__) || defined(__APPLE__) static int fd=-1; if (fd==-1) fd=open("/dev/urandom",O_RDONLY); if (fd!=-1){ unsigned int tmp; if (read(fd,&tmp,4)!=4){ bctbx_error("Reading /dev/urandom failed."); }else return tmp; }else bctbx_error("Could not open /dev/urandom"); # ifdef HAVE_ARC4RANDOM return arc4random(); // fallback to arc4random(). # endif #elif defined(HAVE_ARC4RANDOM) return arc4random(); #elif defined(_WIN32) static int initd=0; unsigned int ret; #ifdef _MSC_VER /*rand_s() is pretty nice and simple function but is not wrapped by mingw.*/ if (rand_s(&ret)==0){ return ret; } #else if (bctbx_wincrypto_random(&ret)==0){ return ret; } #endif /* Windows's rand() is unsecure but is used as a fallback*/ if (!initd) { struct timeval tv; bctbx_gettimeofday(&tv,NULL); srand((unsigned int)tv.tv_sec+tv.tv_usec); initd=1; bctbx_warning("bctoolbox: Random generator is using rand(), this is unsecure !"); } return rand()<<16 | rand(); #endif /*fallback to UNIX random()*/ #ifndef _WIN32 return (unsigned int) random(); #endif } bool_t bctbx_is_multicast_addr(const struct sockaddr *addr) { switch (addr->sa_family) { case AF_INET: return IN_MULTICAST(ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr)); case AF_INET6: return IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *) addr)->sin6_addr)); default: return FALSE; } } #ifdef _WIN32 ssize_t bctbx_send(bctbx_socket_t socket, const void *buffer, size_t length, int flags) { return send(socket, (const char *)buffer, (int)length, flags); } ssize_t bctbx_sendto(bctbx_socket_t socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) { return sendto(socket, (const char *)message, (int)length, flags, dest_addr, (int)dest_len); } ssize_t bctbx_recv(bctbx_socket_t socket, void *buffer, size_t length, int flags) { return recv(socket, (char *)buffer, (int)length, flags); } ssize_t bctbx_recvfrom(bctbx_socket_t socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len) { return recvfrom(socket, (char *)buffer, (int)length, flags, address, (int *)address_len); } ssize_t bctbx_read(int fd, void *buf, size_t nbytes) { return (ssize_t)_read(fd, buf, (unsigned int)nbytes); } ssize_t bctbx_write(int fd, const void *buf, size_t nbytes) { return (ssize_t)_write(fd, buf, (unsigned int)nbytes); } #else ssize_t bctbx_send(bctbx_socket_t socket, const void *buffer, size_t length, int flags) { return send(socket, buffer, length, flags); } ssize_t bctbx_sendto(bctbx_socket_t socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) { return sendto(socket, message, length, flags, dest_addr, dest_len); } ssize_t bctbx_recv(bctbx_socket_t socket, void *buffer, size_t length, int flags) { return recv(socket, buffer, length, flags); } ssize_t bctbx_recvfrom(bctbx_socket_t socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len) { return recvfrom(socket, buffer, length, flags, address, address_len); } ssize_t bctbx_read(int fd, void *buf, size_t nbytes) { return read(fd, buf, nbytes); } ssize_t bctbx_write(int fd, const void *buf, size_t nbytes) { return write(fd, buf, nbytes); } #endif static char allocated_by_bctbx_magic[10] = "bctbx"; static struct addrinfo *_bctbx_alloc_addrinfo(int ai_family, int socktype, int proto){ struct addrinfo *ai=(struct addrinfo*)bctbx_malloc0(sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); ai->ai_family=ai_family; ai->ai_socktype=socktype; ai->ai_protocol=proto; ai->ai_addrlen=AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); ai->ai_addr=(struct sockaddr*)(((unsigned char*)ai) + sizeof(struct addrinfo)); ai->ai_canonname = allocated_by_bctbx_magic; /*this is the way we will recognize our own allocated addrinfo structures in bctbx_freeaddrinfo()*/ return ai; } static struct addrinfo *convert_to_v4mapped(const struct addrinfo *ai){ struct addrinfo *res=NULL; const struct addrinfo *it; struct addrinfo *v4m=NULL; struct addrinfo *last=NULL; for (it=ai;it!=NULL;it=it->ai_next){ struct sockaddr_in6 *sin6; struct sockaddr_in *sin; v4m=_bctbx_alloc_addrinfo(AF_INET6, it->ai_socktype, it->ai_protocol); v4m->ai_flags|=AI_V4MAPPED; sin6=(struct sockaddr_in6*)v4m->ai_addr; sin=(struct sockaddr_in*)it->ai_addr; sin6->sin6_family=AF_INET6; ((uint8_t*)&sin6->sin6_addr)[10]=0xff; ((uint8_t*)&sin6->sin6_addr)[11]=0xff; memcpy(((uint8_t*)&sin6->sin6_addr)+12,&sin->sin_addr,4); sin6->sin6_port=sin->sin_port; if (last){ last->ai_next=v4m; }else{ res=v4m; } last=v4m; } return res; } #if defined(__ANDROID__) || defined(_WIN32) /* * SHAME !!! bionic's getaddrinfo does not implement the AI_V4MAPPED flag ! * It is declared in header file but rejected by the implementation. * The code below is to emulate a _compliant_ getaddrinfo for android. **/ /** * SHAME AGAIN !!! Win32's implementation of getaddrinfo is bogus ! * it is not able to return an IPv6 addrinfo from an IPv4 address when AI_V4MAPPED is set ! **/ struct addrinfo *addrinfo_concat(struct addrinfo *a1, struct addrinfo *a2){ struct addrinfo *it; struct addrinfo *last=NULL; for (it=a1;it!=NULL;it=it->ai_next){ last=it; } if (last){ last->ai_next=a2; return a1; }else return a2; } int bctbx_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res){ if (hints && hints->ai_family!=AF_INET && hints->ai_flags & AI_V4MAPPED){ struct addrinfo *res6=NULL; struct addrinfo *res4=NULL; struct addrinfo lhints={0}; int err; if (hints) memcpy(&lhints,hints,sizeof(lhints)); lhints.ai_flags &= ~(AI_ALL | AI_V4MAPPED); /*remove the unsupported flags*/ lhints.ai_family = AF_INET6; err = getaddrinfo(node, service, &lhints, &res6); if (hints->ai_flags & AI_ALL) { lhints.ai_family=AF_INET; err=getaddrinfo(node, service, &lhints, &res4); if (err==0){ struct addrinfo *v4m=convert_to_v4mapped(res4); freeaddrinfo(res4); res4=v4m; } *res=addrinfo_concat(res6,res4); if (*res) err=0; } return err; } return getaddrinfo(node, service, hints, res); } #else int bctbx_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res){ int result = getaddrinfo(node, service, hints, res); #if __APPLE__ if (*res && (*res)->ai_family == AF_INET6) { struct sockaddr_in6* sockaddr = (struct sockaddr_in6*)(*res)->ai_addr; if (sockaddr->sin6_port == 0 && service) { int possible_port = atoi(service); if (possible_port > 0 && possible_port <= 65535) { bctbx_message("Apple nat64 getaddrinfo bug, fixing port to [%i]",possible_port); sockaddr->sin6_port = htons(possible_port); } } } #endif return result; } #endif int bctbx_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) { #if __APPLE__ /* What an unpleasant surprise... It appears getnameinfo from Apple is calling inet_ntoa internally that is not thread-safe: * https://opensource.apple.com/source/Libc/Libc-583/net/FreeBSD/inet_ntoa.c * http://www.educatedguesswork.org/2009/02/well_thats_an_unpleasant_surpr.html */ int i; int err; for (i = 0; i < 50; i++) { err = getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); if (!(host && strstr(host, "[inet_ntoa error]"))) return err; } return EAI_AGAIN; #else return getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); #endif } static void _bctbx_addrinfo_to_ip_address_error(int err, char *ip, size_t ip_size) { bctbx_error("getnameinfo() error: %s", gai_strerror(err)); strncpy(ip, "", ip_size); } struct addrinfo* bctbx_addrinfo_sort(struct addrinfo *ais) { bctbx_list_t* v6 = NULL; bctbx_list_t* v4_mapped = NULL; bctbx_list_t* v4 = NULL; bctbx_list_t *it = NULL; struct addrinfo* res0 = NULL; struct addrinfo* res = NULL; struct addrinfo* ai = NULL; //sort by type for (ai = ais; ai != NULL; ) { struct addrinfo* next = ai->ai_next; struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)ai->ai_addr; if (ai->ai_family == AF_INET6) { if (IN6_IS_ADDR_V4MAPPED(&sock_in6->sin6_addr)) { v4_mapped = bctbx_list_prepend(v4_mapped, ai); } else { v6 = bctbx_list_prepend(v6, ai); } } else { v4 = bctbx_list_prepend(v4, ai); } ai->ai_next = NULL ; ai = next; } v6 = bctbx_list_concat(v6, v4_mapped); v6 = bctbx_list_concat(v6, v4); for (it = v6; it != NULL; it = it->next) { if (res0 == NULL) { res0 = res = (struct addrinfo*)it->data; } else { res->ai_next = (struct addrinfo*)it->data; res = res->ai_next; } } if (res) res->ai_next = NULL; bctbx_list_free(v6); return res0; } int bctbx_addrinfo_to_ip_address(const struct addrinfo *ai, char *ip, size_t ip_size, int *port){ char serv[16]; int err=bctbx_getnameinfo(ai->ai_addr,(socklen_t)ai->ai_addrlen,ip,(socklen_t)ip_size,serv,(socklen_t)sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV); if (err!=0) _bctbx_addrinfo_to_ip_address_error(err, ip, ip_size); if (port) *port=atoi(serv); return 0; } int bctbx_addrinfo_to_printable_ip_address(const struct addrinfo *ai, char *printable_ip, size_t printable_ip_size) { char ip[64]; char serv[16]; int err = bctbx_getnameinfo(ai->ai_addr, (socklen_t)ai->ai_addrlen, ip, sizeof(ip), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); if (err != 0) _bctbx_addrinfo_to_ip_address_error(err, ip, sizeof(ip)); if (ai->ai_family == AF_INET) snprintf(printable_ip, printable_ip_size, "%s:%s", ip, serv); else if (ai->ai_family == AF_INET6) snprintf(printable_ip, printable_ip_size, "[%s]:%s", ip, serv); return 0; } int bctbx_sockaddr_to_ip_address(const struct sockaddr *sa, socklen_t salen, char *ip, size_t ip_size, int *port) { struct addrinfo ai = { 0 }; ai.ai_addr = (struct sockaddr*)sa; ai.ai_addrlen = salen; ai.ai_family = sa->sa_family; return bctbx_addrinfo_to_ip_address(&ai, ip, ip_size, port); } int bctbx_sockaddr_to_printable_ip_address(struct sockaddr *sa, socklen_t salen, char *printable_ip, size_t printable_ip_size) { if ((sa->sa_family == 0) || (salen == 0)) { snprintf(printable_ip, printable_ip_size, "no-addr"); return 0; } else { struct addrinfo ai = { 0 }; ai.ai_addr = sa; ai.ai_addrlen = salen; ai.ai_family = sa->sa_family; return bctbx_addrinfo_to_printable_ip_address(&ai, printable_ip, printable_ip_size); } } static struct addrinfo * _bctbx_name_to_addrinfo(int family, int socktype, const char *ipaddress, int port, int numeric_only){ struct addrinfo *res=NULL; struct addrinfo hints={0}; char serv[10]; int err; snprintf(serv,sizeof(serv),"%i",port); hints.ai_family=family; if (numeric_only) hints.ai_flags=AI_NUMERICSERV|AI_NUMERICHOST; hints.ai_socktype=socktype; if (family == AF_INET6) { hints.ai_flags |= AI_V4MAPPED; hints.ai_flags |= AI_ALL; } err=bctbx_getaddrinfo(ipaddress,serv,&hints,&res); if (err!=0){ if (!numeric_only || err!=EAI_NONAME) bctbx_error("%s(%s): getaddrinfo failed: %s",__FUNCTION__, ipaddress, gai_strerror(err)); return NULL; } //sort result if (res) res = bctbx_addrinfo_sort(res); return res; } struct addrinfo * bctbx_name_to_addrinfo(int family, int socktype, const char *name, int port){ struct addrinfo * res = NULL; #if defined(__ANDROID__) // This is to workaround possible ANR on Android /*"main" prio=5 tid=1 Native | group="main" sCount=1 dsCount=0 obj=0x75d55258 self=0x231c03fa00 | sysTid=27931 nice=-10 cgrp=default sched=0/0 handle=0x2320165a98 | state=S schedstat=( 0 0 0 ) utm=303 stm=69 core=1 HZ=100 | stack=0x48ae2c5000-0x48ae2c7000 stackSize=8MB | held mutexes= kernel: __switch_to+0x70/0x7c kernel: unix_stream_recvmsg+0x254/0x6f8 kernel: sock_aio_read.part.9+0xe4/0x110 kernel: sock_aio_read+0x20/0x30 kernel: do_sync_read+0x70/0xa8 kernel: vfs_read+0xb0/0x140 kernel: SyS_read+0x54/0xa4 kernel: el0_svc_naked+0x24/0x28 native: #00 pc 000000000006b6e4 /system/lib64/libc.so (read+4) native: #01 pc 00000000000731f8 /system/lib64/libc.so (__sread+44) native: #02 pc 0000000000076fac /system/lib64/libc.so (__srefill+260) native: #03 pc 0000000000076e00 /system/lib64/libc.so (fread+272) native: #04 pc 000000000002fcf0 /system/lib64/libc.so (android_getaddrinfofornetcontext+2356) native: #05 pc 000000000002f33c /system/lib64/libc.so (getaddrinfo+56) native: #06 pc 000000000001e754 /data/app/com.meetme-1/lib/arm64/libbctoolbox.so (bctbx_getaddrinfo+164) native: #07 pc 000000000001ecb4 /data/app/com.meetme-1/lib/arm64/libbctoolbox.so (???) native: #08 pc 0000000000018edc /data/app/com.meetme-1/lib/arm64/libortp.so (???) native: #09 pc 000000000009dd10 /data/app/com.meetme-1/lib/arm64/libmediastreamer_voip.so (audio_stream_start_from_io+132) native: #10 pc 00000000007948d0 /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate19MediaSessionPrivate16startAudioStreamENS_11CallSession5StateEb+3508) native: #11 pc 00000000007956e8 /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate19MediaSessionPrivate12startStreamsENS_11CallSession5StateE+944) native: #12 pc 00000000007839cc /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate19MediaSessionPrivate13updateStreamsEP19SalMediaDescriptionNS_11CallSession5StateE+692) native: #13 pc 000000000078513c /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate19MediaSessionPrivate13remoteRingingEv+932) native: #14 pc 0000000000852f9c /data/app/com.meetme-1/lib/arm64/liblinphone.so (???) native: #15 pc 00000000007d79c4 /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate9SalCallOp17processResponseCbEPvPK24belle_sip_response_event+788) native: #16 pc 00000000007e4bb4 /data/app/com.meetme-1/lib/arm64/liblinphone.so (_ZN15LinphonePrivate3Sal22processResponseEventCbEPvPK24belle_sip_response_event+1092) native: #17 pc 00000000008fdd2c /data/app/com.meetme-1/lib/arm64/liblinphone.so (belle_sip_client_transaction_notify_response+520) native: #18 pc 00000000008f7494 /data/app/com.meetme-1/lib/arm64/liblinphone.so (belle_sip_provider_dispatch_message+1376) native: #19 pc 00000000008e0760 /data/app/com.meetme-1/lib/arm64/liblinphone.so (???) native: #20 pc 00000000008dee1c /data/app/com.meetme-1/lib/arm64/liblinphone.so (belle_sip_channel_process_data+344) native: #21 pc 00000000008d5b10 /data/app/com.meetme-1/lib/arm64/liblinphone.so (belle_sip_main_loop_run+772) native: #22 pc 00000000008d5d54 /data/app/com.meetme-1/lib/arm64/liblinphone.so (belle_sip_main_loop_sleep+72) native: #23 pc 000000000086b9f8 /data/app/com.meetme-1/lib/arm64/liblinphone.so (linphone_core_iterate+540) native: #24 pc 00000000000743c8 /data/app/com.meetme-1/oat/arm64/base.odex (Java_org_linphone_core_CoreImpl_iterate__J+132)*/ res = _bctbx_name_to_addrinfo(family, socktype, name, port, TRUE); #endif if (res == NULL) { res = _bctbx_name_to_addrinfo(family, socktype, name, port, FALSE); } return res; } struct addrinfo * bctbx_ip_address_to_addrinfo(int family, int socktype, const char *name, int port){ struct addrinfo * res = _bctbx_name_to_addrinfo(family, socktype, name, port, TRUE); #if __APPLE__ /*required for nat64 on apple platform*/ if (res) { /*fine, we are sure that name was an ip address, give a chance to get its nat64 form*/ bctbx_freeaddrinfo(res); res = bctbx_name_to_addrinfo(family, SOCK_STREAM, name, port); } #endif return res; } #ifndef IN6_GET_ADDR_V4MAPPED #define IN6_GET_ADDR_V4MAPPED(sin6_addr) *(unsigned int*)((unsigned char*)(sin6_addr)+12) #endif void bctbx_sockaddr_remove_v4_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len) { if (v6->sa_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)v6; if (IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr)) { struct sockaddr_in *in = (struct sockaddr_in *)result; result->sa_family = AF_INET; in->sin_addr.s_addr = IN6_GET_ADDR_V4MAPPED(&in6->sin6_addr); in->sin_port = in6->sin6_port; *result_len = sizeof(struct sockaddr_in); } else { if (v6 != result) memcpy(result, v6, sizeof(struct sockaddr_in6)); *result_len = sizeof(struct sockaddr_in6); } } else { *result_len = sizeof(struct sockaddr_in); if (v6 != result) memcpy(result, v6, sizeof(struct sockaddr_in)); } } void bctbx_sockaddr_remove_nat64_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len) { if (v6->sa_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)v6; if (htonl(0x0064ff9b) == #ifdef _MSC_VER ((in6->sin6_addr.u.Word[0] << 16) & in6->sin6_addr.u.Word[1]) #elif __APPLE__ in6->sin6_addr.__u6_addr.__u6_addr32[0] #else in6->sin6_addr.s6_addr32[0] #endif ) { struct sockaddr_in *in = (struct sockaddr_in *)result; result->sa_family = AF_INET; in->sin_addr.s_addr = IN6_GET_ADDR_V4MAPPED(&in6->sin6_addr); in->sin_port = in6->sin6_port; *result_len = sizeof(struct sockaddr_in); return; } } /* it was not a NAT64 address: just copy the source address as is, whatever it is.*/ *result_len = v6->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); if (v6 != result) memcpy(result, v6, *result_len); } void bctbx_sockaddr_ipv6_to_ipv4(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len) { bctbx_sockaddr_remove_v4_mapping(v6, result, result_len); } void bctbx_sockaddr_ipv4_to_ipv6(const struct sockaddr *v4, struct sockaddr *result, socklen_t *result_len) { if (v4->sa_family == AF_INET) { struct addrinfo *v4m; struct addrinfo ai = { 0 }; struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)result; ai.ai_addr = (struct sockaddr *)v4; ai.ai_addrlen = sizeof(struct sockaddr_in); ai.ai_family = v4->sa_family; v4m = convert_to_v4mapped(&ai); *result_len = sizeof(struct sockaddr_in6); memcpy(v6, v4m->ai_addr, *result_len); bctbx_freeaddrinfo(v4m); } } char *bctbx_replace(char *str, char c, char n) { char *s = str; while (*s) { if (*s == c) { *s = n; } ++s; } return str; } char * bctbx_concat(const char *str, ...) { va_list ap; size_t allocated = 100; char *result = (char *) malloc (allocated); if (result != NULL) { char *newp; char *wp; const char* s; va_start (ap, str); wp = result; for (s = str; s != NULL; s = va_arg (ap, const char *)) { size_t len = strlen (s); /* Resize the allocated memory if necessary. */ if (wp + len + 1 > result + allocated) { intptr_t current_offset = wp - result; allocated = (allocated + len) * 2; newp = (char *) realloc (result, allocated); if (newp == NULL) { free (result); return NULL; } wp = newp + current_offset; result = newp; } memcpy (wp, s, len); wp +=len; } /* Terminate the result string. */ *wp++ = '\0'; /* Resize memory to the optimal size. */ newp = realloc (result, wp - result); if (newp != NULL) result = newp; va_end (ap); } return result; } bool_t bctbx_sockaddr_equals(const struct sockaddr * sa, const struct sockaddr * sb) { if (sa->sa_family != sb->sa_family) return FALSE; if (sa->sa_family == AF_INET) { if ((((struct sockaddr_in*)sa)->sin_addr.s_addr != ((struct sockaddr_in*)sb)->sin_addr.s_addr || ((struct sockaddr_in*)sa)->sin_port != ((struct sockaddr_in*)sb)->sin_port)) return FALSE; } else if (sa->sa_family == AF_INET6) { if (memcmp(&((struct sockaddr_in6*)sa)->sin6_addr , &((struct sockaddr_in6*)sb)->sin6_addr , sizeof(struct in6_addr)) !=0 || ((struct sockaddr_in6*)sa)->sin6_port != ((struct sockaddr_in6*)sb)->sin6_port) return FALSE; } else { bctbx_warning ("Cannot compare family type [%d]", sa->sa_family); return FALSE; } return TRUE; } static const char *ai_family_to_string(int af) { switch(af) { case AF_INET: return "AF_INET"; case AF_INET6: return "AF_INET6"; case AF_UNSPEC: return "AF_UNSPEC"; default: return "invalid address family"; } } static int get_local_ip_for_with_connect(int type, const char *dest, int port, char *result, size_t result_len) { int err, tmp; struct addrinfo hints; struct addrinfo *res = NULL; struct sockaddr_storage addr; struct sockaddr *p_addr = (struct sockaddr *)&addr; bctbx_socket_t sock; socklen_t s; char port_str[6] = { 0 }; memset(&hints, 0, sizeof(hints)); hints.ai_family = type; hints.ai_socktype = SOCK_DGRAM; /*hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME;*/ snprintf(port_str, sizeof(port_str), "%i", port); err = getaddrinfo(dest, port_str, &hints, &res); if (err != 0) { bctbx_error("getaddrinfo() error for %s: %s",dest, gai_strerror(err)); return -1; } if (res == NULL) { bctbx_error("bug: getaddrinfo returned nothing."); return -1; } sock = socket(res->ai_family, SOCK_DGRAM, 0); if (sock == (bctbx_socket_t)-1) { bctbx_error("get_local_ip_for_with_connect() could not create [%s] socket: %s", ai_family_to_string(res->ai_family), getSocketError()); return -1; } tmp = 1; err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (SOCKET_OPTION_VALUE)&tmp, sizeof(int)); if (err == -1) bctbx_warning("Error in setsockopt: %s", getSocketError()); err = connect(sock, res->ai_addr, (int)res->ai_addrlen); if (err == -1) { /* The network isn't reachable. We don't display the error as it is the case that we want to check in normal operation. */ if ( getSocketErrorCode() != BCTBX_ENETUNREACH && getSocketErrorCode() != BCTBX_EHOSTUNREACH && getSocketErrorCode() != BCTBX_EPROTOTYPE) bctbx_error("Error in connect: %s", getSocketError()); freeaddrinfo(res); bctbx_socket_close(sock); return -1; } freeaddrinfo(res); res = NULL; s = sizeof(addr); err = getsockname(sock, (struct sockaddr *)&addr, &s); if (err != 0) { bctbx_error("Error in getsockname: %s", getSocketError()); bctbx_socket_close(sock); return -1; } if (p_addr->sa_family == AF_INET) { struct sockaddr_in *p_sin = (struct sockaddr_in *)p_addr; if (p_sin->sin_addr.s_addr == 0) { bctbx_socket_close(sock); return -1; } } err = bctbx_getnameinfo((struct sockaddr *)&addr, s, result, (socklen_t)result_len, NULL, 0, NI_NUMERICHOST); if (err != 0) bctbx_error("getnameinfo error: %s", gai_strerror(err)); /* Avoid ipv6 link-local addresses */ if ((p_addr->sa_family == AF_INET6) && (strchr(result, '%') != NULL)) { strcpy(result, "::1"); bctbx_socket_close(sock); return -1; } bctbx_socket_close(sock); return 0; } int bctbx_get_local_ip_for(int type, const char *dest, int port, char *result, size_t result_len) { strncpy(result, (type == AF_INET) ? "127.0.0.1" : "::1", result_len); if (dest == NULL) { if (type == AF_INET) dest="87.98.157.38"; /* A public IP address */ else dest = "2a00:1450:8002::68"; } if (port == 0) port = 5060; return get_local_ip_for_with_connect(type, dest, port, result, result_len); } void _bctbx_freeaddrinfo(struct addrinfo *res){ struct addrinfo *it,*next_it; for(it=res;it!=NULL;it=next_it){ next_it=it->ai_next; bctbx_free(it); } } void bctbx_freeaddrinfo(struct addrinfo *res){ struct addrinfo *it; struct addrinfo *previt = NULL; struct addrinfo *beginit = res; bool_t looking_for_allocated_by_bctbx = (res->ai_canonname == allocated_by_bctbx_magic) ? TRUE : FALSE; for (it = res; it != NULL; it = it->ai_next) { if ((looking_for_allocated_by_bctbx == TRUE) && (it->ai_canonname != allocated_by_bctbx_magic)) { if (previt) { previt->ai_next = NULL; _bctbx_freeaddrinfo(beginit); looking_for_allocated_by_bctbx = FALSE; beginit = it; } } else if ((looking_for_allocated_by_bctbx == FALSE) && (it->ai_canonname == allocated_by_bctbx_magic)) { if (previt) { previt->ai_next = NULL; freeaddrinfo(beginit); looking_for_allocated_by_bctbx = TRUE; beginit = it; } } previt = it; } if (looking_for_allocated_by_bctbx == TRUE) _bctbx_freeaddrinfo(beginit); else freeaddrinfo(beginit); } uint8_t bctbx_char_to_byte(uint8_t input_char) { /* 0-9 */ if (input_char>0x29 && input_char<0x3A) { return input_char - 0x30; } /* a-f */ if (input_char>0x60 && input_char<0x67) { return input_char - 0x57; /* 0x57 = 0x61(a) + 0x0A*/ } /* A-F */ if (input_char>0x40 && input_char<0x47) { return input_char - 0x37; /* 0x37 = 0x41(a) + 0x0A*/ } /* shall never arrive here, string is not Hex*/ return 0; } uint8_t bctbx_byte_to_char(uint8_t input_byte) { uint8_t input_byte_crop = input_byte&0x0F; /* restrict the input value to range [0-15] */ /* 0-9 */ if(input_byte_crop<0x0A) { return input_byte_crop+0x30; } /* a-f */ return input_byte_crop + 0x57; } void bctbx_str_to_uint8(uint8_t *output_bytes, const uint8_t *input_string, size_t input_string_length) { size_t i; for (i=0; i>4)&0x0F); output_string[2*i+1] = bctbx_byte_to_char(input_bytes[i]&0x0F); } } void bctbx_uint32_to_str(uint8_t output_string[9], uint32_t input_uint32) { output_string[0] = bctbx_byte_to_char((uint8_t)((input_uint32>>28)&0x0F)); output_string[1] = bctbx_byte_to_char((uint8_t)((input_uint32>>24)&0x0F)); output_string[2] = bctbx_byte_to_char((uint8_t)((input_uint32>>20)&0x0F)); output_string[3] = bctbx_byte_to_char((uint8_t)((input_uint32>>16)&0x0F)); output_string[4] = bctbx_byte_to_char((uint8_t)((input_uint32>>12)&0x0F)); output_string[5] = bctbx_byte_to_char((uint8_t)((input_uint32>>8)&0x0F)); output_string[6] = bctbx_byte_to_char((uint8_t)((input_uint32>>4)&0x0F)); output_string[7] = bctbx_byte_to_char((uint8_t)((input_uint32)&0x0F)); output_string[8] = '\0'; } uint32_t bctbx_str_to_uint32(const uint8_t *input_string) { return (((uint32_t)bctbx_char_to_byte(input_string[0]))<<28) | (((uint32_t)bctbx_char_to_byte(input_string[1]))<<24) | (((uint32_t)bctbx_char_to_byte(input_string[2]))<<20) | (((uint32_t)bctbx_char_to_byte(input_string[3]))<<16) | (((uint32_t)bctbx_char_to_byte(input_string[4]))<<12) | (((uint32_t)bctbx_char_to_byte(input_string[5]))<<8) | (((uint32_t)bctbx_char_to_byte(input_string[6]))<<4) | (((uint32_t)bctbx_char_to_byte(input_string[7]))); } void bctbx_uint64_to_str(uint8_t output_string[17], uint64_t input_uint64) { output_string[0] = bctbx_byte_to_char((uint8_t)((input_uint64>>60)&0x0F)); output_string[1] = bctbx_byte_to_char((uint8_t)((input_uint64>>56)&0x0F)); output_string[2] = bctbx_byte_to_char((uint8_t)((input_uint64>>52)&0x0F)); output_string[3] = bctbx_byte_to_char((uint8_t)((input_uint64>>48)&0x0F)); output_string[4] = bctbx_byte_to_char((uint8_t)((input_uint64>>44)&0x0F)); output_string[5] = bctbx_byte_to_char((uint8_t)((input_uint64>>40)&0x0F)); output_string[6] = bctbx_byte_to_char((uint8_t)((input_uint64>>36)&0x0F)); output_string[7] = bctbx_byte_to_char((uint8_t)((input_uint64>>32)&0x0F)); output_string[8] = bctbx_byte_to_char((uint8_t)((input_uint64>>28)&0x0F)); output_string[9] = bctbx_byte_to_char((uint8_t)((input_uint64>>24)&0x0F)); output_string[10] = bctbx_byte_to_char((uint8_t)((input_uint64>>20)&0x0F)); output_string[11] = bctbx_byte_to_char((uint8_t)((input_uint64>>16)&0x0F)); output_string[12] = bctbx_byte_to_char((uint8_t)((input_uint64>>12)&0x0F)); output_string[13] = bctbx_byte_to_char((uint8_t)((input_uint64>>8)&0x0F)); output_string[14] = bctbx_byte_to_char((uint8_t)((input_uint64>>4)&0x0F)); output_string[15] = bctbx_byte_to_char((uint8_t)((input_uint64)&0x0F)); output_string[16] = '\0'; } uint64_t bctbx_str_to_uint64(const uint8_t input_string[17]) { return (((uint64_t)bctbx_char_to_byte(input_string[0]))<<60) | (((uint64_t)bctbx_char_to_byte(input_string[1]))<<56) | (((uint64_t)bctbx_char_to_byte(input_string[2]))<<52) | (((uint64_t)bctbx_char_to_byte(input_string[3]))<<48) | (((uint64_t)bctbx_char_to_byte(input_string[4]))<<44) | (((uint64_t)bctbx_char_to_byte(input_string[5]))<<40) | (((uint64_t)bctbx_char_to_byte(input_string[6]))<<36) | (((uint64_t)bctbx_char_to_byte(input_string[7]))<<32) | (((uint64_t)bctbx_char_to_byte(input_string[8]))<<28) | (((uint64_t)bctbx_char_to_byte(input_string[9]))<<24) | (((uint64_t)bctbx_char_to_byte(input_string[10]))<<20) | (((uint64_t)bctbx_char_to_byte(input_string[11]))<<16) | (((uint64_t)bctbx_char_to_byte(input_string[12]))<<12) | (((uint64_t)bctbx_char_to_byte(input_string[13]))<<8) | (((uint64_t)bctbx_char_to_byte(input_string[14]))<<4) | (((uint64_t)bctbx_char_to_byte(input_string[15]))); } #if defined(__ANDROID__) int mblen(const char* s, size_t n) { mbstate_t state = {}; return (int)mbrlen(s, n, &state); } int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); } #endif int bctbx_strcasecmp(const char *a, const char *b) { if (a == NULL) a = ""; if (b == NULL) b = ""; return strcasecmp(a, b); } int bctbx_strcmp(const char *a, const char *b) { if (a == NULL) a = ""; if (b == NULL) b = ""; return strcmp(a, b); } #if !defined(_WIN32) void bctbx_set_stack_trace_hooks(bool_t use_bctbx_hooks){ bctbx_warning("bctbx_set_stack_trace_hooks(): not implemented on this platform."); } #endif bctoolbox-5.2.0/src/utils/regex.cc000066400000000000000000000047331434566643100171040ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "bctoolbox/logging.h" #include "bctoolbox/regex.h" /* This part is needed since CentOS7 have an old gcc compiler. TODO: Remove this code when all supported platorms have gcc 4.9.0 or more */ #if __cplusplus >= 201103L && \ (!defined(__GLIBCXX__) || (__cplusplus >= 201402L) || \ (defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \ defined(_GLIBCXX_REGEX_STATE_LIMIT) || \ (defined(_GLIBCXX_RELEASE) && \ _GLIBCXX_RELEASE > 4))) && \ !defined(__ANDROID__) || defined(_WIN32) #define HAVE_WORKING_REGEX 1 #else #define HAVE_WORKING_REGEX 0 #include #endif extern "C" bool_t bctbx_is_matching_regex_log(const char *entry, const char* regex, bool_t show_log){ #if HAVE_WORKING_REGEX try { std::regex entry_regex(regex, std::regex_constants::extended | std::regex_constants::nosubs); std::cmatch m; return std::regex_match(entry, m, entry_regex); } catch (const std::regex_error& e) { if(show_log) bctbx_error("Could not compile regex '%s': %s", regex, e.what()); return FALSE; } #else regex_t regex_pattern; char err_msg[256]; int res; res = regcomp(®ex_pattern, regex, REG_EXTENDED | REG_NOSUB); if(res != 0) { if(show_log) { regerror(res, ®ex_pattern, err_msg, sizeof(err_msg)); bctbx_error("Could not compile regex '%s': %s", regex, err_msg); } return FALSE; } res = regexec(®ex_pattern, entry, 0, NULL, 0); regfree(®ex_pattern); return (res != REG_NOMATCH); #endif } extern "C" bool_t bctbx_is_matching_regex(const char *entry, const char* regex) { return bctbx_is_matching_regex_log(entry, regex, TRUE); } bctoolbox-5.2.0/src/utils/utils.cc000066400000000000000000000072731434566643100171340ustar00rootroot00000000000000/* * Copyright (c) 2016-2021 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox/utils.hh" #include using namespace std; #ifdef BCTBX_WINDOWS_UWP using namespace Windows::System; using namespace Windows::Foundation; #endif vector bctoolbox::Utils::split (const string &str, const string &delimiter) { vector out; size_t pos = 0, oldPos = 0; for (; (pos = str.find(delimiter, pos)) != string::npos; oldPos = pos + delimiter.length(), pos = oldPos) out.push_back(str.substr(oldPos, pos - oldPos)); out.push_back(str.substr(oldPos)); return out; } string bctoolbox::Utils::fold (const string &str) { string output = str; size_t crlf = 0; size_t next_crlf = 0; const char *endline = "\r\n"; while (next_crlf != string::npos) { next_crlf = output.find(endline, crlf); if (next_crlf != string::npos) { if (next_crlf - crlf > 75) { output.insert(crlf + 74, "\r\n "); crlf += 76; } else { crlf = next_crlf + 2; } } } return output; } string bctoolbox::Utils::unfold (const string &str) { string output = str; const char *endline = "\r\n"; size_t crlf = output.find(endline); if (crlf == string::npos) { endline = "\n"; crlf = output.find(endline); } while (crlf != string::npos) { if (isspace(output[crlf + strlen(endline)])) { output.erase(crlf, strlen(endline) + 1); } else { crlf += strlen(endline); } crlf = output.find(endline, crlf); } return output; } void bctoolbox::Utils::replace(string& source, const string& from, const string& to, const bool& recursive){ size_t start_pos = 0; while((start_pos = source.find(from, start_pos)) != string::npos) { source.replace(start_pos, from.length(), to); if(recursive) start_pos += to.length(); } } std::string bctoolbox::Utils::getMemoryReportAsString() { std::ostringstream ossReport; #ifdef WIN32 // Use to convert bytes to MB const int division = 1048576; MEMORYSTATUSEX memoryStatus; memoryStatus.dwLength = sizeof (memoryStatus); GlobalMemoryStatusEx (&memoryStatus); ossReport << "Memory stats (MB): Usage=" << memoryStatus.dwMemoryLoad << ", Total physical=" << memoryStatus.ullTotalPhys/division << ", Free physical=" << memoryStatus.ullAvailPhys/division << ", Total paging file=" << memoryStatus.ullTotalPageFile/division << ", Free paging file=" << memoryStatus.ullAvailPageFile/division << ", Total virtual=" << memoryStatus.ullTotalVirtual/division << ", Free virtual=" << memoryStatus.ullAvailVirtual/division << ", Free extended=" << memoryStatus.ullAvailExtendedVirtual/division; #ifdef BCTBX_WINDOWS_UWP ossReport << " | UWP App Memory (MB): Usage=" << MemoryManager::AppMemoryUsage/division << ", Usage limit=" << MemoryManager::AppMemoryUsageLimit/division << ", Usage level=" << (int)MemoryManager::AppMemoryUsageLevel << ", Expected usage limit=" << MemoryManager::ExpectedAppMemoryUsageLimit/division << ", Free=" << (long)( MemoryManager::AppMemoryUsageLimit - MemoryManager::AppMemoryUsage )/division; #endif #endif return ossReport.str(); } bctoolbox-5.2.0/src/utils/win_utils.cc000066400000000000000000000214511434566643100200030ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox/port.h" #include "bctoolbox/logging.h" #include #include #include #include #include #include #include #include #include #include #pragma comment(lib, "dbghelp.lib") using namespace std; //------------------------------------------------- //---------------- PLATFORM --------------------- //------------------------------------------------- #ifdef BCTBX_WINDOWS_UWP std::string GetBackTrace(int SkipFrames) { std::stringstream result; constexpr unsigned int TRACE_MAX_STACK_FRAMES = 99; void *stack[TRACE_MAX_STACK_FRAMES]; ULONG hash; const int numFrames = CaptureStackBackTrace(SkipFrames + 1, TRACE_MAX_STACK_FRAMES, stack, &hash); constexpr auto MODULE_BUF_SIZE = 4096U; char modulePath[MODULE_BUF_SIZE]; result << "Stack hash: " << hash << "\n"; for (int i = 0; i < numFrames; ++i) { void *moduleBaseVoid = nullptr; RtlPcToFileHeader(stack[i], &moduleBaseVoid); auto moduleBase = (const unsigned char *)moduleBaseVoid; const char *moduleFilename = modulePath; result << i << ": "; if (moduleBase != nullptr) { GetModuleFileNameA((HMODULE)moduleBase, modulePath, MODULE_BUF_SIZE); result << moduleFilename << "+" << (uint32_t)((unsigned char*)stack[i] - moduleBase) << "\n"; }else result << moduleFilename << "+" << (uint64_t)stack[i] << "\n"; } return result.str(); } // Hooks void _signal_hook(int u ){ // Skip 8 useless stack frames bctbx_error("UWP Stack trace %d :\n%s\n", u, GetBackTrace(8).c_str()); } //------------------------------------ #else struct StackFrame { DWORD64 address; std::string name; std::string module; unsigned int line; std::string file; }; std::vector getStackTrace() { #if _WIN64 DWORD machine = IMAGE_FILE_MACHINE_AMD64; #else DWORD machine = IMAGE_FILE_MACHINE_I386; #endif HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); if (SymInitialize(process, NULL, TRUE) == FALSE) { bctbx_error(__FUNCTION__ ": Failed to call SymInitialize."); return std::vector(); } SymSetOptions(SYMOPT_LOAD_LINES); CONTEXT context = {}; context.ContextFlags = CONTEXT_FULL; RtlCaptureContext(&context); #if _WIN64 STACKFRAME frame = {}; frame.AddrPC.Offset = context.Rip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Rbp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Rsp; frame.AddrStack.Mode = AddrModeFlat; #else STACKFRAME frame = {}; frame.AddrPC.Offset = context.Eip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Ebp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Esp; frame.AddrStack.Mode = AddrModeFlat; #endif bool first = true; std::vector frames; while (StackWalk(machine, process, thread, &frame, &context , NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) { StackFrame f = {}; f.address = frame.AddrPC.Offset; #if _WIN64 DWORD64 moduleBase = 0; #else DWORD moduleBase = 0; #endif moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset); char moduelBuff[MAX_PATH]; if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH)) { f.module = bctbx_basename(moduelBuff); } else { f.module = "Unknown Module"; } #if _WIN64 DWORD64 offset = 0; #else DWORD offset = 0; #endif char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255]; PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer; symbol->SizeOfStruct = (sizeof IMAGEHLP_SYMBOL) + 255; symbol->MaxNameLength = 254; if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol)) { f.name = symbol->Name; } else { f.name = "Unknown Name"; } IMAGEHLP_LINE line; line.SizeOfStruct = sizeof(IMAGEHLP_LINE); DWORD offset_ln = 0; if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line)) { f.file = line.FileName; f.line = line.LineNumber; } else { f.line = -1; } if (!first) frames.push_back(f); first = false; } SymCleanup(process); return frames; } // Hooks void _signal_hook( int u){ std::stringstream buff; buff << __FUNCTION__ << ": General Fault: '" << std::to_string(u) << "'! \n"; buff << "\n"; std::vector stack = getStackTrace(); buff << "Callstack: \n"; for (unsigned int i = 0; i < stack.size(); i++) buff << "0x" << std::hex << stack[i].address << ": " << stack[i].name << "(" << stack[i].line << ") in " << stack[i].module << "\n"; bctbx_error(buff.str().c_str()); } #endif //------------------------------------------------- //------------------------------------------------- //------------------------------------------------- //--------------------------------- // Static old handlers : Use static and not capture in lambda because if a capture is present, the lambda cannot be converted to a function pointer and then, it is unusable by ahndlers static LPTOP_LEVEL_EXCEPTION_FILTER gOldUnhandledException = NULL; static std::terminate_handler gOldTerminateHandler = NULL; static _crt_signal_t gOldABRT = NULL; static _crt_signal_t gOldFPE = NULL; static _crt_signal_t gOldILL = NULL; static _crt_signal_t gOldEGV = NULL; static _crt_signal_t gOldTERM = NULL; // Generic Hooks LONG WINAPI _unhandled_exceptions_hook(_EXCEPTION_POINTERS* pExp) { _signal_hook(SIGABRT); if(gOldUnhandledException) return gOldUnhandledException(pExp); else return EXCEPTION_EXECUTE_HANDLER; } void _abrt_signal_hook( int u){ _signal_hook(u); if(gOldABRT ) gOldABRT(u); } void _fpe_signal_hook( int u){ _signal_hook(u); if(gOldFPE ) gOldFPE(u); } void _ill_signal_hook( int u){ _signal_hook(u); if(gOldILL ) gOldILL(u); } void _egv_signal_hook( int u){ _signal_hook(u); if(gOldEGV) gOldEGV(u); } void _term_signal_hook( int u){ _signal_hook(u); if(gOldTERM ) gOldTERM(u); } void _terminate_hook(){ _signal_hook(SIGTERM); if(gOldTerminateHandler) gOldTerminateHandler(); else std::abort(); } void setSignalHook(int sig, _crt_signal_t function, _crt_signal_t& oldFunction){ auto old = signal(sig, function); if( old != function ) oldFunction = old; } void setTerminateHook(std::terminate_handler function, std::terminate_handler& oldFunction){ auto old = std::set_terminate(function); if( old != function ) oldFunction = old; } void setUnhandledExceptionHook(LPTOP_LEVEL_EXCEPTION_FILTER function, LPTOP_LEVEL_EXCEPTION_FILTER& oldFunction){ auto old = SetUnhandledExceptionFilter(function); if( old != function ) oldFunction = old; } void bctbx_set_stack_trace_hooks(bool_t use_bctbx_hooks) { // Try to take account of all handlers if(use_bctbx_hooks){ // Signal setSignalHook(SIGABRT, _abrt_signal_hook, gOldABRT); // Abnormal termination setSignalHook(SIGFPE, _fpe_signal_hook, gOldFPE); // Floating-point error setSignalHook(SIGILL, _ill_signal_hook, gOldILL); // Illegal instruction setSignalHook(SIGSEGV, _egv_signal_hook, gOldEGV); // Illegal storage access setSignalHook(SIGTERM, _term_signal_hook, gOldTERM); // Terminate request // Termination setTerminateHook(_terminate_hook,gOldTerminateHandler ); // Top level exception setUnhandledExceptionHook(_unhandled_exceptions_hook, gOldUnhandledException); //atexit(_test_atexit);// UWP doesn't catch it }else{// if 'old' is set, set 'old' in handlers and unset it to avoid overrides. if(gOldABRT) setSignalHook(SIGABRT, gOldABRT, gOldABRT); // Abnormal termination gOldABRT = NULL; if(gOldFPE) setSignalHook(SIGFPE, gOldFPE, gOldFPE); // Floating-point error gOldFPE = NULL; if(gOldILL) setSignalHook(SIGILL, gOldILL, gOldILL); // Illegal instruction gOldILL = NULL; if(gOldEGV) setSignalHook(SIGSEGV, gOldEGV, gOldEGV); // Illegal storage access gOldEGV = NULL; if(gOldTERM) setSignalHook(SIGTERM, gOldTERM, gOldTERM); // Terminate request gOldTERM = NULL; // Termination if(gOldTerminateHandler) setTerminateHook(gOldTerminateHandler,gOldTerminateHandler ); gOldTerminateHandler = NULL; // Top level exception if(gOldUnhandledException) setUnhandledExceptionHook(gOldUnhandledException, gOldUnhandledException); gOldUnhandledException = NULL; } } bctoolbox-5.2.0/src/vconnect.c000066400000000000000000000126641434566643100163100ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/vconnect.h" #include "bctoolbox/port.h" #include "bctoolbox/logging.h" #include #include #include /**============================================================ * Wrappers around default libc calls used to handle windows * differences in function prototypes. *=============================================================*/ static bctbx_socket_t vsocket_socket(int socket_family, int socket_type, int protocol){ return socket(socket_family, socket_type, protocol); } static int vsocket_connect(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len){ #ifdef _WIN32 return connect(sock, address, (int)address_len); #else return connect(sock, address, address_len); #endif } static int vsocket_bind(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len){ #ifdef _WIN32 return bind(sock, address, (int)address_len); #else return bind(sock, address, address_len); #endif } static int vsocket_getsockname(bctbx_socket_t sockfd, struct sockaddr *addr, socklen_t *addrlen){ #ifdef _WIN32 return getsockname(sockfd, addr, (int*)addrlen); #else return getsockname(sockfd, addr, addrlen); #endif } static int vsocket_getsockopt(bctbx_socket_t sockfd, int level, int optname, void *optval, socklen_t* optlen){ #ifdef _WIN32 return getsockopt(sockfd, level, optname, (char*)optval, (int*)optlen); #else return getsockopt(sockfd, level, optname, optval, optlen); #endif } static int vsocket_setsockopt(bctbx_socket_t sockfd, int level, int optname, const void *optval, socklen_t optlen){ #ifdef _WIN32 return setsockopt(sockfd, level, optname, (char*)optval, (int)optlen); #else return setsockopt(sockfd, level, optname, optval, optlen); #endif } static int vsocket_close(bctbx_socket_t sock){ #ifdef _WIN32 return closesocket(sock); #else return close(sock); #endif } static int vsocket_shutdown(bctbx_socket_t sock, int how){ return shutdown(sock, how); } #if !defined(_WIN32) && !defined(_WIN32_WCE) static char* vsocket_error(int err){ return strerror (err); } #else static char* vsocket_error(int err){ return (char*)__bctbx_getWinSocketError(err); } #endif /**=================================================== * Socket API default methods pointer definition. * ===================================================*/ static const bctbx_vsocket_methods_t bcSocketAPI = { vsocket_socket, vsocket_connect, vsocket_bind, vsocket_getsockname, vsocket_getsockopt, vsocket_setsockopt, vsocket_close, vsocket_error, vsocket_shutdown, }; static bctbx_vsocket_api_t bcvSocket = { "bctbx_socket", /* vSockName */ &bcSocketAPI, /*pSocketMethods */ }; /* Pointer to default socket methods initialized to standard libc implementation here.*/ static bctbx_vsocket_api_t *pDefaultvSocket = &bcvSocket; bctbx_socket_t bctbx_socket(int socket_family, int socket_type, int protocol){ return pDefaultvSocket->pSocketMethods->pFuncSocket( socket_family, socket_type, protocol); } int bctbx_socket_close(bctbx_socket_t sock){ return pDefaultvSocket->pSocketMethods->pFuncClose(sock); } int bctbx_bind(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len){ return pDefaultvSocket->pSocketMethods->pFuncBind(sock, address, address_len); } int bctbx_connect(bctbx_socket_t sock, const struct sockaddr *address, socklen_t address_len){ return pDefaultvSocket->pSocketMethods->pFuncConnect(sock, address, address_len); } int bctbx_getsockname(bctbx_socket_t sockfd, struct sockaddr *addr, socklen_t *addrlen){ return pDefaultvSocket->pSocketMethods->pFuncGetSockName(sockfd, addr, addrlen); } int bctbx_getsockopt(bctbx_socket_t sockfd, int level, int optname, void *optval, socklen_t* optlen){ return pDefaultvSocket->pSocketMethods->pFuncGetSockOpt(sockfd, level, optname, optval, optlen); } int bctbx_setsockopt(bctbx_socket_t sockfd, int level, int optname, const void *optval, socklen_t optlen){ return pDefaultvSocket->pSocketMethods->pFuncSetSockOpt(sockfd, level, optname, optval, optlen); } int bctbx_shutdown(bctbx_socket_t sock, int how){ return pDefaultvSocket->pSocketMethods->pFuncShutdown(sock, how); } char* bctbx_socket_error(int err){ return pDefaultvSocket->pSocketMethods->pFuncGetError(err); } void bctbx_vsocket_api_set_default(bctbx_vsocket_api_t *my_vsocket_api) { if (my_vsocket_api == NULL){ pDefaultvSocket = &bcvSocket; } else { pDefaultvSocket = my_vsocket_api ; } } bctbx_vsocket_api_t* bctbx_vsocket_api_get_default(void) { return pDefaultvSocket; } bctbx_vsocket_api_t* bctbx_vsocket_api_get_standard(void) { return &bcvSocket; } bctoolbox-5.2.0/src/vfs/000077500000000000000000000000001434566643100151125ustar00rootroot00000000000000bctoolbox-5.2.0/src/vfs/vfs.c000066400000000000000000000346111434566643100160610ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/vfs.h" #include "bctoolbox/crypto.h" #include "bctoolbox/vfs_standard.h" #include "bctoolbox/port.h" #include "bctoolbox/logging.h" #include #include #include static ssize_t bctbx_file_flush(bctbx_vfs_file_t *pFile); /* Pointer to default VFS initialized to standard VFS implemented here.*/ static bctbx_vfs_t *pDefaultVfs = &bcStandardVfs; /* bcStandardVfs is defined int vfs_standard.h*/ /** * Create flags (int) from mode(char*). * @param mode Can be r, r+, w+, w * @return flags (integer). */ static int set_flags(const char* mode) { int flags = 0 ; /* flags to pass to open() call */ if (strcmp(mode, "r") == 0) { flags = O_RDONLY; } else if ((strcmp(mode, "r+") == 0) || (strcmp(mode, "w+") == 0)) { flags = O_RDWR; } else if(strcmp(mode, "w") == 0) { flags = O_WRONLY; } return flags | O_CREAT; } ssize_t bctbx_file_write(bctbx_vfs_file_t* pFile, const void *buf, size_t count, off_t offset) { ssize_t ret; if (pFile != NULL) { if (bctbx_file_flush(pFile) < 0) { // make sure our write is not overwritten by a page flush return BCTBX_VFS_ERROR; } ret = pFile->pMethods->pFuncWrite(pFile, buf, count, offset); if (ret == BCTBX_VFS_ERROR) { bctbx_error("bctbx_file_write file error"); return BCTBX_VFS_ERROR; } else if (ret < 0) { bctbx_error("bctbx_file_write error %s", strerror(-(ret))); return BCTBX_VFS_ERROR; } pFile->gSize = 0; // cancel get cache, as it might be dirty now return ret; } return BCTBX_VFS_ERROR; } ssize_t bctbx_file_write2(bctbx_vfs_file_t* pFile, const void *buf, size_t count) { ssize_t ret = bctbx_file_write(pFile, buf, count, pFile->offset); if (ret != BCTBX_VFS_ERROR) { bctbx_file_seek(pFile, ret, SEEK_CUR); } return ret; } static int file_open(bctbx_vfs_t* pVfs, bctbx_vfs_file_t* pFile, const char *fName, const int oflags) { int ret = BCTBX_VFS_ERROR; if (pVfs && pFile ) { ret = pVfs->pFuncOpen(pVfs, pFile, fName, oflags); if (ret == BCTBX_VFS_ERROR) { bctbx_error("bctbx_file_open: Error file handle"); } else if (ret < 0 ) { bctbx_error("bctbx_file_open: Error open %s", strerror(-(ret))); ret = BCTBX_VFS_ERROR; } } return ret; } bctbx_vfs_file_t* bctbx_file_open(bctbx_vfs_t *pVfs, const char *fName, const char *mode) { int ret; bctbx_vfs_file_t* p_ret = (bctbx_vfs_file_t*)bctbx_malloc(sizeof(bctbx_vfs_file_t)); int oflags = set_flags(mode); if (p_ret) { memset(p_ret, 0, sizeof(bctbx_vfs_file_t)); ret = file_open(pVfs, p_ret, fName, oflags); if (ret == BCTBX_VFS_OK) return p_ret; } if (p_ret) bctbx_free(p_ret); return NULL; } bctbx_vfs_file_t* bctbx_file_open2(bctbx_vfs_t *pVfs, const char *fName, const int openFlags) { int ret; bctbx_vfs_file_t *p_ret = (bctbx_vfs_file_t *)bctbx_malloc(sizeof(bctbx_vfs_file_t)); if (p_ret) { memset(p_ret, 0, sizeof(bctbx_vfs_file_t)); ret = file_open(pVfs, p_ret, fName, openFlags); if (ret == BCTBX_VFS_OK) return p_ret; } if (p_ret) bctbx_free(p_ret); return NULL; } static ssize_t bctbx_file_flush(bctbx_vfs_file_t *pFile) { if (pFile->fSize == 0) { return 0; } size_t fSize = pFile->fSize; // save the size so we could restore it if something goes wrong pFile->fSize = 0; // set it to 0 now so when we call write it won't enter in an infinite loop(as write will call flush) ssize_t r = bctbx_file_write(pFile, pFile->fPage, fSize, pFile->fPageOffset); if (r < 0) { // something went wrong, restore the page size pFile->fSize = fSize; } return r; } ssize_t bctbx_file_read(bctbx_vfs_file_t *pFile, void *buf, size_t count, off_t offset) { int ret = BCTBX_VFS_ERROR; if (pFile) { if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } ret = pFile->pMethods->pFuncRead(pFile, buf, count, offset); /*check if error : in this case pErrSvd is initialized*/ if (ret == BCTBX_VFS_ERROR) { bctbx_error("bctbx_file_read: error bctbx_vfs_file_t"); } else if (ret < 0) { bctbx_error("bctbx_file_read: Error read %s", strerror(-(ret))); ret = BCTBX_VFS_ERROR; } } return ret; } ssize_t bctbx_file_read2(bctbx_vfs_file_t *pFile, void *buf, size_t count) { ssize_t ret = bctbx_file_read(pFile, buf, count, pFile->offset); if (ret != BCTBX_VFS_ERROR) { bctbx_file_seek(pFile, ret, SEEK_CUR); } return ret; } int bctbx_file_close(bctbx_vfs_file_t *pFile) { int ret = BCTBX_VFS_ERROR; if (pFile) { if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } /* clean the fprint and getline cache as they might hold the plain version of an encrypted file */ if (bctbx_file_is_encrypted(pFile)) { bctbx_clean(pFile->fPage, BCTBX_VFS_PRINTF_PAGE_SIZE); bctbx_clean(pFile->gPage, BCTBX_VFS_GETLINE_PAGE_SIZE); } ret = pFile->pMethods->pFuncClose(pFile); if (ret != 0) { bctbx_error("bctbx_file_close: Error %s freeing file handle anyway", strerror(-(ret))); } } bctbx_free(pFile); return ret; } int bctbx_file_sync(bctbx_vfs_file_t *pFile) { int ret = BCTBX_VFS_ERROR; if (pFile) { if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } ret = pFile->pMethods->pFuncSync(pFile); if (ret != BCTBX_VFS_OK) { bctbx_error("bctbx_file_sync: Error %s ", strerror(-(ret))); } } return ret; } int64_t bctbx_file_size(bctbx_vfs_file_t *pFile) { int64_t ret = BCTBX_VFS_ERROR; if (pFile){ if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } ret = pFile->pMethods->pFuncFileSize(pFile); if (ret < 0) bctbx_error("bctbx_file_size: Error file size %s", strerror((int)-(ret))); } return ret; } int bctbx_file_truncate(bctbx_vfs_file_t *pFile, int64_t size) { int ret = BCTBX_VFS_ERROR; if (pFile){ if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } ret = pFile->pMethods->pFuncTruncate(pFile, size); if (ret < 0) bctbx_error("bctbx_file_truncate: Error truncate %s", strerror((int)-(ret))); } return ret; } ssize_t bctbx_file_fprintf(bctbx_vfs_file_t *pFile, off_t offset, const char *fmt, ...) { char *ret = NULL; va_list args; ssize_t r = BCTBX_VFS_ERROR; size_t count = 0; va_start(args, fmt); ret = bctbx_strdup_vprintf(fmt, args); if (ret != NULL) { va_end(args); count = strlen(ret); if (offset != 0) { bctbx_file_flush(pFile); pFile->offset = offset; } // Shall we write in cache or in the file if (count + pFile->fSize < BCTBX_VFS_PRINTF_PAGE_SIZE) { // Data fits in current page memcpy(pFile->fPage+pFile->fSize, ret, count); if (pFile->fSize == 0) { pFile->fPageOffset = pFile->offset; } bctbx_free(ret); pFile->offset += (off_t)count; pFile->fSize += count; pFile->gSize = 0; // cancel get cache, as it might be dirty now return (ssize_t)count; } else if (pFile->fSize > 0){ // There is a cache but the new data won't fit in : write the cache and the new data char *buf = bctbx_malloc(count+pFile->fSize); // allocate a temporary buffer, to store the current cache and the data to be written if (buf == NULL) { bctbx_free(ret); return BCTBX_VFS_ERROR; } memcpy(buf, pFile->fPage, pFile->fSize); // concatenate the cache and new data memcpy(buf+pFile->fSize, ret, count); bctbx_free(ret); r = bctbx_file_write(pFile, buf, pFile->fSize + count, pFile->fPageOffset); // write all bctbx_free(buf); if (r<0) { return r; } pFile->fSize = 0; // f cache is now empty pFile->offset += (off_t)count; pFile->gSize = 0; // cancel get cache, as it might be dirty now return (ssize_t)count; } // no cache and more than one page to write, just write it r = bctbx_file_write(pFile, ret, count, pFile->offset); bctbx_free(ret); if (r > 0) pFile->offset += r; } return r; } /** * This function manages the offset stored in pFile and used by the bctbx_file_get_nxtline function * It has no effect on the current offset in the underlying file which we have no use of */ off_t bctbx_file_seek(bctbx_vfs_file_t *pFile, off_t offset, int whence) { off_t ret = BCTBX_VFS_ERROR; if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } if (pFile) { switch (whence) { case SEEK_SET: // set to the given offset ret = offset; break; case SEEK_CUR: // set relative to the current offset ret = pFile->offset + offset; break; case SEEK_END: // set relative to the end of file ret = (off_t)bctbx_file_size(pFile) + offset; break; default: bctbx_error("Encrypted VFS: Invalid whence value in bcSeek: %d", whence); return BCTBX_VFS_ERROR; } pFile->offset = ret; return ret; } return BCTBX_VFS_ERROR; } static char *findNextLine(const char *buf) { char *pNextLine = NULL; char *pNextLineR = NULL; char *pNextLineN = NULL; pNextLineR = strchr(buf, '\r'); pNextLineN = strchr(buf, '\n'); if ((pNextLineR != NULL) && (pNextLineN != NULL)) pNextLine = MIN(pNextLineR, pNextLineN); else if (pNextLineR != NULL) pNextLine = pNextLineR; else if (pNextLineN != NULL) pNextLine = pNextLineN; return pNextLine; } /* a generic implementation of get_nxt_line * if a vfs does not specify one, use this one */ /** * Gets a line of max_len length and stores it to the allocaed buffer s. * Reads at most max_len characters from the file descriptor associated with the argument pFile * and looks for an end of line character (\r or \n). Stores the line found * into the buffer pointed by s. * Modifies the open file offset using pFile->offset. * * @param pFile File handle pointer. * @param s Buffer where to store the line. * @param max_len Maximum number of characters to read in one fetch. * @return size of line read, 0 if empty */ static int bctbx_generic_get_nxtline(bctbx_vfs_file_t *pFile, char *s, int max_len) { int64_t ret; int sizeofline; char *pNextLine = NULL; if (!pFile) { return BCTBX_VFS_ERROR; } if (s == NULL || max_len < 1) { return BCTBX_VFS_ERROR; } if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } sizeofline = 0; // If we have a cached page and the current offset is in this page if ((pFile->gSize > 0) && (pFile->gPageOffset<=pFile->offset) && (pFile->gPageOffset + (off_t)pFile->gSize > pFile->offset)) { // look for a new line in the cache const char *c = pFile->gPage + (pFile->offset - pFile->gPageOffset); pNextLine = findNextLine(c); if (pNextLine) { /* Got a line! Comments use \n to describe EOL while it can be \r too */ sizeofline = (int)(pNextLine - c + 1); // 1 one so it actually includes the \n char pFile->offset += sizeofline; // offset to next beginning of line if ((pNextLine[0] == '\r') && (pNextLine[1] == '\n')) { // take into account the \r\n case, this case can pass underdetected if page ends in between, not a problem we will just get an extra empty line pFile->offset += 1; } memcpy(s,c,sizeofline-1); // copy all before the \n s[sizeofline-1] = '\0'; // add a \0 at the end where the \n was return sizeofline; // return size including the termination, so an empty line returns 1 (0 is for EOF) } else { // No end of line found, did we reach the EOF? if (pFile->gPage[pFile->gSize-1] == 0x04) { // 0x04 is EOT in ASCII, put in cache to signal the end of file sizeofline = (int)(pFile->gSize - (pFile->offset - pFile->gPageOffset) -1); // size does not include the EOT char(which is part of the page size). so -1 pFile->offset += sizeofline; // offset now points on the EOT char memcpy(s,c,sizeofline); // copy everything before the EOT s[sizeofline] = '\0'; return sizeofline; // Can be 0 if we were already pointing on EOT when we enter this call } } } // not in cache, read it from file s[max_len-1] = '\0'; /* Read returns 0 if end of file is found */ ret = bctbx_file_read(pFile, s, max_len - 1, pFile->offset); if (ret > 0) { size_t readSize = (size_t)ret; // Store it in cache if (max_len - 1 < BCTBX_VFS_GETLINE_PAGE_SIZE) { memcpy(pFile->gPage, s, readSize); pFile->gPageOffset = pFile->offset; pFile->gSize = readSize; if (retgPage[readSize] = 0x04; // 0x04 is ASCII for EOT, use it to store the EOF in the buffer pFile->gSize += 1; // the EOT is part of the cached page } pFile->gPage[pFile->gSize] = '\0'; } else { bctbx_warning("bctbx_get_nxtline given a max size value %d bigger than cache size (%d), please adjust one or the other", max_len, BCTBX_VFS_GETLINE_PAGE_SIZE); } pNextLine = findNextLine(s); if (pNextLine) { /* Got a line! */ sizeofline = (int)(pNextLine - s + 1); /* offset to next beginning of line*/ pFile->offset += sizeofline; if ((pNextLine[0] == '\r') && (pNextLine[1] == '\n')) {/*take into account the \r\n" case*/ pFile->offset += 1; } *pNextLine = '\0'; } else { /*did not find end of line char, is EOF?*/ sizeofline = (int)ret; pFile->offset += sizeofline; s[readSize] = '\0'; } } else if (ret < 0) { bctbx_error("bcGetLine error"); } return sizeofline; } int bctbx_file_get_nxtline(bctbx_vfs_file_t *pFile, char *s, int maxlen) { if (pFile) { /* if the vfs does not implement this method, use the generic one */ if (bctbx_file_flush(pFile) < 0) { return BCTBX_VFS_ERROR; } if (pFile->pMethods && pFile->pMethods->pFuncGetLineFromFd) { return pFile->pMethods->pFuncGetLineFromFd(pFile, s, maxlen); } else { return bctbx_generic_get_nxtline(pFile, s, maxlen); } } return BCTBX_VFS_ERROR; } bool_t bctbx_file_is_encrypted(bctbx_vfs_file_t *pFile) { if (pFile && pFile->pMethods && pFile->pMethods->pFuncIsEncrypted){ return pFile->pMethods->pFuncIsEncrypted(pFile); } return FALSE; } void bctbx_vfs_set_default(bctbx_vfs_t *my_vfs) { pDefaultVfs = my_vfs; } bctbx_vfs_t* bctbx_vfs_get_default(void) { return pDefaultVfs; } bctbx_vfs_t* bctbx_vfs_get_standard(void) { return &bcStandardVfs; } bctoolbox-5.2.0/src/vfs/vfs_encrypted.cc000066400000000000000000001076361434566643100203110ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox/vfs.h" #include "bctoolbox/vfs_encrypted.hh" #include "vfs_encryption_module.hh" #include "vfs_encryption_module_dummy.hh" #include "vfs_encryption_module_aes256gcm_sha256.hh" #include "bctoolbox/vfs_standard.h" #include "bctoolbox/logging.h" #include #include // MSVC does not define O_ACCMODE... #ifndef O_ACCMODE #define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR) #endif // someone does include the evil windef.h so we must undef the min and max macros to be able to use std::min and std::max #undef min #undef max using namespace bctoolbox; /** Helpers function: this part of the code must be updated to add modules */ static size_t moduleFileHeaderSize(const uint16_t suite) { switch (suite) { case static_cast(EncryptionSuite::dummy): return VfsEncryptionModuleDummy::moduleFileHeaderSize(); case static_cast(EncryptionSuite::aes256gcm128_sha256): return VfsEM_AES256GCM_SHA256::moduleFileHeaderSize(); case static_cast(EncryptionSuite::unset): case static_cast(EncryptionSuite::plain): default: return 0; } } // is called at file creation when the encryption suite is set using encryptionSuiteSet static std::shared_ptr make_VfsEncryptionModule(const EncryptionSuite suite) { switch (suite) { case EncryptionSuite::dummy: return std::make_shared(); case EncryptionSuite::aes256gcm128_sha256: return std::make_shared(); case EncryptionSuite::plain: return nullptr; case EncryptionSuite::unset: default: throw EVFS_EXCEPTION<<"Encrypted FS: unsupported encryption scheme "<(suite); } } // is called when an encrypted file header is parsed static std::shared_ptr make_VfsEncryptionModule(const uint16_t suite, const std::vector moduleFileHeader) { switch (suite) { /* all supported scheme must be listed here */ case static_cast(EncryptionSuite::dummy): return std::make_shared(moduleFileHeader); case static_cast(EncryptionSuite::aes256gcm128_sha256): return std::make_shared(moduleFileHeader); case static_cast(EncryptionSuite::unset): case static_cast(EncryptionSuite::plain): default: throw EVFS_EXCEPTION<<"Encrypted FS: unsupported encryption scheme "< BCENCRYPTEDFS={0x62, 0x63, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x46, 0x73}; static constexpr uint16_t BcEncFS_v0100=0x0100; /* header cannot be less than this size, even for an empty file */ static constexpr int64_t baseFileHeaderSize=29; static constexpr size_t defaultChunkSize = 4096; // default chunk size in bytes /** * Initialiase the static callback property */ EncryptedVfsOpenCb VfsEncryption::s_openCallback = nullptr; VfsEncryption::VfsEncryption(bctbx_vfs_file_t *stdFp, const std::string &filename, int openFlags, int accessMode) : mVersionNumber(BcEncFS_v0100), // default version number is the current one mChunkSize(0), // set to 0 at creation, is will be populated by parseHeader if there is one. If we are creating a file, let a chance to the callback to set the chunk size. m_module(nullptr), // encryption module is set by callback or when parsing the header mHeaderExtensionSize(0), mFilename(filename), mFileSize(0), mEncryptExistingPlainFile(false), mIntegrityFullCheck(false), mAccessMode(accessMode), pFileStd(stdFp) { if (stdFp == NULL) throw EVFS_EXCEPTION<<"Cannot create a vfs encrytion object, vfs pointer is null"; // If the file exists, read the header to check it is an encrypted file and gets its encrypted policy // if the file is plain, set the mFileSize so then we now we already have a file but it is plain bool createFile = true; if (bctbx_file_size(stdFp) > 0) { parseHeader(); createFile = false; } /* if the static callback is set, call it */ if (VfsEncryption::openCallbackGet() != nullptr) { (VfsEncryption::openCallbackGet())(*this); } else { throw EVFS_EXCEPTION << "Encrypted VFS: must provide a callback to setup key material"; } if (m_module == nullptr) { // this is a plain file and we want to keep it this way return; } /* check we have a valid chunk size */ if (mChunkSize == 0) { // this is a file creation and the callback didn't set it mChunkSize = defaultChunkSize; // assign the default one } if (mEncryptExistingPlainFile == true) { // we have a plain file to encrypt // create a temporary file std::string tmpFilename(mFilename); tmpFilename.append(".evfs_tmp"); // make sure this file does not exists std::remove(tmpFilename.data()); auto stdFdTmp = bctbx_file_open2(bctbx_vfs_get_standard(), tmpFilename.data(), O_WRONLY|O_CREAT); // read the whole file chunk by chunk and write their ciphertext to the temp file char *readBuf = static_cast(bctbx_malloc(mChunkSize)); uint64_t index = 0; uint32_t currentChunkIndex = 0; do { // read auto readSize = bctbx_file_read(pFileStd, readBuf, mChunkSize, static_cast(index)); if (readSize < 0) { bctbx_file_close(stdFdTmp); bctbx_free(readBuf); throw EVFS_EXCEPTION<<"Unable to migrate plain file "<encryptChunk(currentChunkIndex, std::vector(readBuf, readBuf+readSize)); // write if (bctbx_file_write(stdFdTmp, rawChunk.data(), rawChunk.size(), (off_t)getChunkOffset(currentChunkIndex)) - rawChunk.size() != 0 ){ bctbx_file_close(stdFdTmp); bctbx_free(readBuf); throw EVFS_EXCEPTION<<"Unable to migrate plain file "< 0 ) { // this is not a file creation if (m_module->checkIntegrity(*this) != true) { throw EVFS_EXCEPTION<<"Integrity check fail while opening file "<0; chunkIndex--) { // start from last chunk std::vector rawData(rawChunkSizeGet()); ssize_t readSize = bctbx_file_read(pFileStd, rawData.data(), rawData.size(), (off_t)getChunkOffset(chunkIndex)); if (readSize >= 0) { rawData.resize(readSize); } else { throw EVFS_EXCEPTION<<"fail to read file while trying to check the full integrity, file_read returned "< plainData(mChunkSize); // decrypt the chunk, if it fails it will generate an exception, let it flow up m_module->decryptChunk(chunkIndex, rawData); } // all clear, update header writeHeader(); BCTBX_SLOGW<<"Encrypted FS: Whole file integrity check successfull, update header with correct file size"; } } } } if (createFile) { writeHeader(); } } VfsEncryption::~VfsEncryption() { if (pFileStd != nullptr) { bctbx_file_close(pFileStd); } } /** * Returns the size of chunks in which the file is divided for encryption */ size_t VfsEncryption::chunkSizeGet() const noexcept { return (mChunkSize==0?defaultChunkSize:mChunkSize); // mChunkSize to 0 means it is not initialised yet, so return the default value in this case }; /** * Set the size, in bytes, of chunks in which the file is divided for encryption * This size must be a multiple of 16. * If the size is set on an existing file and differs from previous setting, an exception is generated * Default chunk size at file creation is 4kB */ void VfsEncryption::chunkSizeSet(const size_t size) { if (size < 16 || size > 1048560) { throw EVFS_EXCEPTION<<"Encrypted VFS cannot set a chunk size "< &secretMaterial) { if (m_module == nullptr) { if (mFileSize > 0 && mAccessMode == O_RDONLY) { // we are opening a read only plain file, ignore the secret material BCTBX_SLOGW<<" Encrypted VFS access a plain file "<setModuleSecretMaterial(secretMaterial); } /** * Set an encryption suite */ void VfsEncryption::encryptionSuiteSet(const EncryptionSuite suite) { if (m_module == nullptr && mFileSize == 0) { // file creation m_module = make_VfsEncryptionModule(suite); } else { // file already exists (if m_filesize!=0 and m_module is nullptr, it is an existing plain file, the encryptionSuiteGet would return plain) if (encryptionSuiteGet() != suite) { if (encryptionSuiteGet() == bctoolbox::EncryptionSuite::plain) { // we want to migrate a plain file to an encrypted one if (mAccessMode != O_RDONLY) { // Do not migrate read-only file mEncryptExistingPlainFile = true; m_module = make_VfsEncryptionModule(suite); } else { BCTBX_SLOGW<<" Encrypted VFS access a plain file "<getEncryptionSuite(); } if (mFileSize > 0) { // we have no suite but file exists -> it is plain return bctoolbox::EncryptionSuite::plain; } return EncryptionSuite::unset; } /* return the size of the raw file */ uint64_t VfsEncryption::rawFileSizeGet() const noexcept { // first compute the number of chunks in our file uint64_t n = 0; // if we have an incomplete chunk if ((mFileSize%mChunkSize)>0) { n = 1; } n += mFileSize/mChunkSize; return mFileSize // actual plain size + n*m_module->getChunkHeaderSize() // all chunks' header size + baseFileHeaderSize + mHeaderExtensionSize + m_module->getModuleFileHeaderSize(); // file header size } /** * Get raw header: encryption module might check integrity on header * This function returns the raw header, without the encryption module part */ const std::vector& VfsEncryption::rawHeaderGet() const noexcept { return r_header; } /** * Parse the file header * Header format for current version (1.00) is: * * - magicnumber: hexadecimal ASCII for bcEncryptedFs: 0x6263456E637279707465644673 : 13 bytes * - version Number: 0xMMmm : 2 bytes * - encryption suite: 2 bytes * - chunk size : 2 bytes : size of a chunk payload in number of 16 bytes blocks - excluding chunk header if any. -> maximum chunk size is 2^16-1*16 = 1 MB - recommended is 4Kb (256 blocks) * - Header extension size: 2 bytes. Flexibility on header size - this version of parser may be able to read newer future versions * - size: clear text file size : 8 bytes. * - [Optionnal Header Extension - future versions of the vfs encryption] * - [Optionnal Encryption module data - size is given by the encryption suite selected] * * * @throw a EvfsException is something goes wrong */ void VfsEncryption::parseHeader() { // check file exists int64_t ret = bctbx_file_size(pFileStd); // bctbx_file_size return a signed value... if (ret0. // read the header r_header = std::vector(baseFileHeaderSize); size_t index=0; if (bctbx_file_read(pFileStd, r_header.data(), baseFileHeaderSize, 0) != baseFileHeaderSize) throw EVFS_EXCEPTION<<"parseHeader: unable to read encrypted vfs header"; // check it starts with the magic number if (!std::equal(BCENCRYPTEDFS.cbegin(), BCENCRYPTEDFS.cend(), r_header.cbegin())) { // file is not starting with our magic number, assume it is plain mFileSize = ret; // set file size so we know that the file exists and is plain m_module = nullptr; return; } index += BCENCRYPTEDFS.size(); // check the version number mVersionNumber = r_header[index]<<8|r_header[index+1]; if ( mVersionNumber != BcEncFS_v0100 ) { BCTBX_SLOGW<<"Encrypted FS trying to open a file version "<(r_header[index])<<56) | (static_cast(r_header[index+1])<<48) | (static_cast(r_header[index+2])<<40) | (static_cast(r_header[index+3])<<32) | (static_cast(r_header[index+4])<<24) | (static_cast(r_header[index+5])<<16) | (static_cast(r_header[index+6])<<8) | static_cast(r_header[index+7]); // get the optional encryption scheme data if needed size_t encryptionModuleDataSize = moduleFileHeaderSize(encryptionSuite); // read the data, the are at offset baseFileHeaderSize + mHeaderExtensionSize auto encryptionSuiteData = std::vector(encryptionModuleDataSize); if (encryptionModuleDataSize != 0) { if (bctbx_file_read(pFileStd, encryptionSuiteData.data(), encryptionModuleDataSize, (off_t)(baseFileHeaderSize+mHeaderExtensionSize)) - encryptionModuleDataSize != 0) { throw EVFS_EXCEPTION<<"Encrypted FS: unable to read encryption scheme data in file header"; } } // instanciate the encryption module m_module = make_VfsEncryptionModule(encryptionSuite, encryptionSuiteData); // check file size match what we have : // If they do not match, check all chunks integrity and update the header. Recovery from failure between write and header update at last write/truncate if (rawFileSizeGet() != fileSize) { BCTBX_SLOGW<<"Encrypted FS: meta data file size "<getModuleFileHeaderSize()); // remove file header size uint64_t chunkNb = 0; if (mFileSize % rawChunkSizeGet() > 0) { chunkNb = 1; } chunkNb += mFileSize / rawChunkSizeGet(); mFileSize -= chunkNb*m_module->getChunkHeaderSize(); BCTBX_SLOGW<<"Encrypted FS: Actual file size seems to be "< header{BCENCRYPTEDFS}; // starts with the magic number header.reserve(baseFileHeaderSize+m_module->getModuleFileHeaderSize()); // add version number header.emplace_back(mVersionNumber>>8); header.emplace_back(mVersionNumber&0xFF); // add encryption suite uint16_t int_suite = static_cast(m_module->getEncryptionSuite()); header.emplace_back(int_suite>>8); header.emplace_back(int_suite&0xFF); // add chunk size (turn it into 16 bytes block number) header.emplace_back(static_cast(((mChunkSize/16)>>8)&0xFF)); header.emplace_back(static_cast((mChunkSize/16)&0xFF)); // add header extension size: none for now header.emplace_back(0x00); header.emplace_back(0x00); // add file size header.emplace_back(static_cast((mFileSize>>56)&0xFF)); header.emplace_back(static_cast((mFileSize>>48)&0xFF)); header.emplace_back(static_cast((mFileSize>>40)&0xFF)); header.emplace_back(static_cast((mFileSize>>32)&0xFF)); header.emplace_back(static_cast((mFileSize>>24)&0xFF)); header.emplace_back(static_cast((mFileSize>>16)&0xFF)); header.emplace_back(static_cast((mFileSize>>8)&0xFF)); header.emplace_back(static_cast(mFileSize&0xFF)); // update header cache (do not cache the encryption module data) // moduleFileHeader shall depends on the file header as it probably authentify it, // so do this update before asking for the encryption module header r_header = header; // add encryption module data auto moduleFileHeader = m_module->getModuleFileHeader(*this); header.insert(header.end(), moduleFileHeader.cbegin(), moduleFileHeader.cend()); // write header to file (to the object file pointer if none is given as parameter) ssize_t ret = bctbx_file_write((fp==nullptr)?pFileStd:fp, header.data(), header.size(), 0); if (ret - header.size() != 0) { // cannot compare directly signed and unsigned... throw EVFS_EXCEPTION<< "Encrypted VFS: something went wrong while writing file header. file_write returns "<getChunkHeaderSize(); }; /** * in which chunk is this offset? */ uint32_t VfsEncryption::getChunkIndex(uint64_t offset) const noexcept { return static_cast(offset/mChunkSize); } /** * @returns the offset, in the actual file, of the begining of the given chunk */ size_t VfsEncryption::getChunkOffset(uint32_t index) const noexcept { return rawChunkSizeGet()*index // all previous chunks + baseFileHeaderSize + mHeaderExtensionSize + m_module->getModuleFileHeaderSize(); } std::vector VfsEncryption::read(size_t offset, size_t count) const { // plain file? if (m_module == nullptr) { std::vector plain(count); auto readSize = bctbx_file_read(pFileStd, plain.data(), plain.size(), (off_t)offset); plain.resize(readSize); return plain; } /* first compute how much of the actual file we must read */ uint32_t firstChunk = getChunkIndex(offset); uint32_t lastChunk = getChunkIndex(offset+count-1); // -1 as we read data from indexes offset to offset + count - 1 size_t offsetInFirstChunk = offset%mChunkSize; // allocate a vector large enough to store all the data to read : number of chunks * size of raw chunk(payload+header) std::vector rawData((lastChunk-firstChunk+1)*rawChunkSizeGet()); /* read all chunks from actual file */ ssize_t readSize = bctbx_file_read(pFileStd, rawData.data(), rawData.size(), (off_t)getChunkOffset(firstChunk)); /* resize rawData to the actual content size - last chunk may be incomplete */ if (readSize >= 0) { rawData.resize(readSize); } else { throw EVFS_EXCEPTION<<"fail to read file "< plainData{}; plainData.reserve((lastChunk-firstChunk+1)*mChunkSize); // decrypt everything we have chunk by chunk, use firstChunk as chunk index while (rawData.size() > m_module->getChunkHeaderSize()) { std::vector plainChunk = m_module->decryptChunk(firstChunk++, std::vector(rawData.cbegin(), rawData.cbegin()+std::min(rawChunkSizeGet(), rawData.size()))); plainData.insert(plainData.end(), plainChunk.cbegin(), plainChunk.cend()); // remove the decrypted chunk rawData.erase(rawData.begin(), rawData.begin()+std::min(rawChunkSizeGet(), rawData.size())); } // return only the requested part plainData.erase(plainData.begin(), plainData.begin()+std::min(offsetInFirstChunk, plainData.size())); // remove unwanted chunk begining if (count &plainData, size_t offset) { // plain file? if (m_module == nullptr) { ssize_t ret = bctbx_file_write(pFileStd, plainData.data(), plainData.size(), (off_t)offset); if ( ret - plainData.size() == 0) { // compare signed and unsigned return plainData.size(); } else { throw EVFS_EXCEPTION<<"plain file fail to write to physical file "<< ret; } } auto plain = plainData; // work on a local copy as we may modify it uint64_t finalFileSize = std::max(mFileSize, static_cast(plain.size()+offset)); // we might need to increase the file size // Are we writing after the end of the file, if yes, prepend with zeros if (offset > mFileSize) { plain.insert(plain.begin(), offset-static_cast(mFileSize), 0); offset = static_cast(mFileSize); } uint32_t firstChunk = getChunkIndex(offset); uint32_t lastChunk = getChunkIndex(offset+plain.size()-1); // -1 as we write data from indexes offset to offset + data size - 1 size_t rawDataSize = (lastChunk-firstChunk+1)*rawChunkSizeGet(); // maximum size used, last chunk might be incomplete std::vector rawData{}; // Store the existing encrypted chunks with header that are overwritten by this operation // Are we overwritting some chunks? size_t readOffset = offset - offset%mChunkSize; // we must start read/write at the begining of a chunk if (readOffsetdecryptChunk(firstChunk, std::vector(rawData.cbegin(), rawData.cbegin() + std::min(rawChunkSizeGet(), rawData.size()))); plain.insert(plain.begin(), plainChunk.cbegin(), plainChunk.cbegin()+(offset-readOffset)); // prepend the begining to our plain buffer } // append the plain buffer if needed: if ((plain.size()%mChunkSize != 0) && (plain.size()+readOffset < mFileSize)){ // We do not have an integer number of chunks to write and we have data after our last written byte auto plainChunk = m_module->decryptChunk(lastChunk, std::vector(rawData.cbegin()+getChunkOffset(lastChunk)-getChunkOffset(firstChunk), rawData.cbegin()+std::min(getChunkOffset(lastChunk+1)-getChunkOffset(firstChunk), rawData.size()))); plain.insert(plain.end(), plainChunk.cbegin()+(plain.size()%mChunkSize), plainChunk.cend()); // append what is over the part we will write. } // encrypt the overwritten chunks std::vector updatedRawData{}; updatedRawData.reserve(rawDataSize); uint32_t currentChunkIndex = firstChunk; while (rawData.size()>0) { // get a chunk to re-encrypt std::vector rawChunk(rawData.cbegin(), rawData.cbegin()+std::min(rawChunkSizeGet(), rawData.size())); // delete it rawData.erase(rawData.begin(), rawData.begin()+std::min(rawChunkSizeGet(), rawData.size())); // re-encrypt m_module->encryptChunk(currentChunkIndex++, rawChunk, std::vector(plain.cbegin(), plain.cbegin()+std::min(mChunkSize, plain.size()))); // delete consumed plain plain.erase(plain.begin(), plain.begin()+std::min(mChunkSize, plain.size())); // store the result updatedRawData.insert(updatedRawData.end(), rawChunk.cbegin(), rawChunk.cend()); } // add new chunks if some data remains in the plain buffer while (plain.size()>0) { auto rawChunk = m_module->encryptChunk(currentChunkIndex++, std::vector(plain.cbegin(), plain.cbegin()+std::min(mChunkSize, plain.size()))); // delete consumed plain plain.erase(plain.begin(), plain.begin()+std::min(mChunkSize, plain.size())); // store the result updatedRawData.insert(updatedRawData.end(), rawChunk.cbegin(), rawChunk.cend()); } // now actually write the rawData in the file ssize_t ret = bctbx_file_write(pFileStd, updatedRawData.data(), updatedRawData.size(), (off_t)getChunkOffset(firstChunk)); if ( ret - updatedRawData.size() == 0) { // compare signed and unsigned mFileSize = finalFileSize; writeHeader(); return plainData.size(); } else { throw EVFS_EXCEPTION<<"fail to write to physical file "<{}, static_cast(newSize)); // write nothing at new size index, the gap is filled with 0 by write return; } if (mFileSize > newSize) { // If the last chunk is modified, we must re-encrypt it if (newSize%mChunkSize != 0) { // allocate a vector large enough to store a complete chunk std::vector rawData(rawChunkSizeGet()); // read the future last chunk from actual file ssize_t readSize = bctbx_file_read(pFileStd, rawData.data(), rawData.size(), (off_t)getChunkOffset(getChunkIndex(newSize))); rawData.resize(readSize); // decrypt it auto plainLastChunk = m_module->decryptChunk(getChunkIndex(newSize), std::vector(rawData.cbegin(), rawData.cbegin()+std::min(rawChunkSizeGet(), rawData.size()))); // truncate the part we don't need anymore plainLastChunk.resize(newSize%mChunkSize); // re-encrypt it m_module->encryptChunk(getChunkIndex(newSize), rawData, std::vector(plainLastChunk.cbegin(), plainLastChunk.cend())); /* write it to the actual file */ if (bctbx_file_write(pFileStd, rawData.data(), rawData.size(), (off_t)getChunkOffset(getChunkIndex(newSize))) - rawData.size() != 0) { throw EVFS_EXCEPTION << "Cannot write file "<pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); delete(ctx); // that will close the file pFile->pUserData=NULL; } return ret; } /** * Simply sync the file contents given through the file handle * Just forward the request to underlying vfs */ static int bcSync(bctbx_vfs_file_t *pFile) { if (pFile && pFile->pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); return bctbx_file_sync(ctx->pFileStd); } return BCTBX_VFS_ERROR; } /** * Read count bytes from the open file given by pFile, starting at offset. * Sets the error errno in the argument pErrSrvd after allocating it * if an error occurred. * @param pFile File handle pointer. * @param buf buffer to write the read bytes to. * @param count number of bytes to read * @param offset file offset where to start reading * @return -errno if erroneous read, number of bytes read (count) on success, * if the error was something else BCTBX_VFS_ERROR otherwise */ static ssize_t bcRead(bctbx_vfs_file_t *pFile, void *buf, size_t count, off_t offset) { if (pFile && pFile->pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); try { auto readBuffer = ctx->read(offset, count); memcpy (buf, readBuffer.data(), readBuffer.size()); return (ssize_t)readBuffer.size(); } catch (EvfsException const &e) { // cannot let raise an exception to a C context BCTBX_SLOGE<<"Encrypted VFS: error while reading "<filenameGet()<<" at offset "<pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); return (ssize_t)ctx->write(std::vector(reinterpret_cast(buf), reinterpret_cast(buf)+count), offset); } return BCTBX_VFS_ERROR; } /** * Returns the file size associated with the file handle pFile. * Return the size of cleartext file * @param pFile File handle pointer. * @return -errno if an error occurred, file size otherwise (can be 0). */ static int64_t bcFileSize(bctbx_vfs_file_t *pFile) { int ret = BCTBX_VFS_ERROR; if (pFile && pFile->pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); return ctx->fileSizeGet(); } return ret; } /* ** Truncate a file * @param pFile File handle pointer. * @param new_size Extends the file with null bytes if it is superior to the file's size * truncates the file otherwise. * @return -errno if an error occurred, 0 otherwise. */ static int bcTruncate(bctbx_vfs_file_t *pFile, int64_t new_size){ if (new_size<0) return BCTBX_VFS_ERROR; // vfs interface force usage of int64_t as new size... int ret = BCTBX_VFS_ERROR; if (pFile && pFile->pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); ctx->truncate(new_size); return 0; } return ret; } /* ** is a file encrypted or plain * @param pFile File handle pointer. * @return true if the file is encrypted */ static bool_t bcIsEncrypted(bctbx_vfs_file_t *pFile) { if (pFile && pFile->pUserData) { VfsEncryption *ctx = static_cast(pFile->pUserData); return (ctx->encryptionSuiteGet() == bctoolbox::EncryptionSuite::plain)?FALSE:TRUE; } return FALSE; } static const bctbx_io_methods_t bcio = { bcClose, /* pFuncClose */ bcRead, /* pFuncRead */ bcWrite, /* pFuncWrite */ bcTruncate, /* pFuncTruncate */ bcFileSize, /* pFuncFileSize */ bcSync, NULL, // use the generic get next line function bcIsEncrypted }; static int bcOpen(bctbx_vfs_t *pVfs, bctbx_vfs_file_t *pFile, const char *fName, int openFlags) { VfsEncryption *ctx = nullptr; bctbx_vfs_file_t *stdFp = nullptr; try { if (pFile == NULL || fName == NULL) { return BCTBX_VFS_ERROR; } int accessMode = openFlags&O_ACCMODE; // encrypted vfs encapsulates the standard one, open the file with it // File cannot be writeonly as write operation may imply read/decrypt/write if ((openFlags&O_ACCMODE) == O_WRONLY) { openFlags &=~O_ACCMODE; openFlags |=O_RDWR; } stdFp = bctbx_file_open2(bctbx_vfs_get_standard(), fName, openFlags); if (stdFp == NULL) return BCTBX_VFS_ERROR; pFile->pMethods = &bcio; ctx = new VfsEncryption(stdFp, fName, openFlags, accessMode); /* store the encryption context in the vfs UserData */ pFile->pUserData = static_cast(ctx); return BCTBX_VFS_OK; } catch (EvfsException const &e) {// caller is most likely a C file(vfs.c), so swallow all exceptions if (stdFp != nullptr) { bctbx_file_close(stdFp); } delete(ctx); BCTBX_SLOGE<<"Encrypted VFS can't open File "<. */ #ifndef BCTBX_VFS_ENCRYPTION_MODULE_HH #define BCTBX_VFS_ENCRYPTION_MODULE_HH #include "bctoolbox/vfs_encrypted.hh" namespace bctoolbox { /** * Define the interface any encryption suite must provide */ class VfsEncryptionModule { public: /** * @return the size in bytes of the chunk header */ virtual size_t getChunkHeaderSize() const noexcept = 0; /** * @return the size in bytes of file header module data */ virtual size_t getModuleFileHeaderSize() const noexcept = 0; /** * @return the encryptionSuite implemented by the module */ virtual EncryptionSuite getEncryptionSuite() const noexcept = 0; /** * Get from the module the data to store in the file header */ virtual const std::vector getModuleFileHeader(const VfsEncryption &fileContext) const = 0; /** * Set in the module, the secret material used for encryption */ virtual void setModuleSecretMaterial(const std::vector &secret) = 0; /** * Get the size of the secret material needed by this module */ virtual size_t getSecretMaterialSize() const noexcept = 0; /** * Decrypt a data chunk * @param[in] a vector which size shall be chunkHeaderSize + chunkSize holding the raw data read from disk * @return the decrypted data chunk */ virtual std::vector decryptChunk(const uint32_t chunkIndex, const std::vector &rawChunk) = 0; /** * ReEncrypt a data chunk * @param[in/out] rawChunk The existing encrypted chunk * @param[in] plainData The plain text to be encrypted */ virtual void encryptChunk(const uint32_t chunkIndex, std::vector &rawChunk, const std::vector &plainData) = 0; /** * Encrypt a new data chunk * @param[in] chunkIndex The chunk index * @param[in] plainData The plain text to be encrypted * @return the encrypted chunk */ virtual std::vector encryptChunk(const uint32_t chunkIndex, const std::vector &plainData) = 0; /** * Check the integrity over the whole file * @param[in] fileContext a way to access the file content * * @return true if the integrity check successfully passed, false otherwise */ virtual bool checkIntegrity(const VfsEncryption &fileContext) = 0; virtual ~VfsEncryptionModule() {}; }; } // namespace bctoolbox #endif // BCTBX_VFS_ENCRYPTION_MODULE_HH bctoolbox-5.2.0/src/vfs/vfs_encryption_module_aes256gcm_sha256.cc000066400000000000000000000206041434566643100247040ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "vfs_encryption_module_aes256gcm_sha256.hh" #include #include #include "bctoolbox/crypto.hh" #include "bctoolbox/crypto.h" // bctbx_clean #include "bctoolbox/logging.h" using namespace bctoolbox; /** * Constants associated to this encryption module */ /** Chunk Header in this module holds: Auth tag(16 bytes), IV : 12 bytes, Encryption Counter 4 bytes */ static constexpr size_t chunkAuthTagSize = AES256GCM128::tagSize(); static constexpr size_t chunkIVSize = 12; static constexpr size_t chunkHeaderSize = chunkAuthTagSize + chunkIVSize; /** * File header holds: fileSalt (16 bytes), file header auth tag(32 bytes) */ static constexpr size_t fileSaltSize = 16; static constexpr size_t fileAuthTagSize = 32; static constexpr size_t fileHeaderSize = fileSaltSize + fileAuthTagSize; /** * The master Key is expected to be 32 bytes */ static constexpr size_t masterKeySize=32; /** constructor called at file creation */ VfsEM_AES256GCM_SHA256::VfsEM_AES256GCM_SHA256() : mRNG(std::make_shared()), // start the local RNG mFileSalt(mRNG->randomize(fileSaltSize)) // generate a random file Salt { } /** constructor called when opening an existing file */ VfsEM_AES256GCM_SHA256::VfsEM_AES256GCM_SHA256(const std::vector &fileHeader) : mRNG(std::make_shared()), // start the local RNG mFileSalt(std::vector(fileSaltSize)) { if (fileHeader.size() != fileHeaderSize) { throw EVFS_EXCEPTION<<"The AES256GCM128-SHA256 encryption module expect a fileHeader of size "< VfsEM_AES256GCM_SHA256::getModuleFileHeader(const VfsEncryption &fileContext) const { if (sFileHeaderHMACKey.empty()) { throw EVFS_EXCEPTION<<"The AES256GCM128-SHA256 encryption module cannot generate its file header without master key"; } // Only the actual file header is to authenticate, the module file header holds the global salt used to derive the key feed to HMAC authenticating the file header // so it is useless to authenticate it auto tag = HMAC(sFileHeaderHMACKey, fileContext.rawHeaderGet()); // Append the actual file salt value to the tag auto ret = mFileSalt; ret.insert(ret.begin(), tag.cbegin(), tag.cend()); return ret; } void VfsEM_AES256GCM_SHA256::setModuleSecretMaterial(const std::vector &secret) { if (secret.size() != masterKeySize) { throw EVFS_EXCEPTION<<"The AES256GCM128 SHA256 encryption module expect a secret material of size "<(mFileSalt, sMasterKey, "EVFS file Header", masterKeySize); } /** * Derive the key from master key for the given chunkIndex: * HKDF(fileSalt || ChunkIndex, master Key, "EVFS chunk") * * @param[in] chunkIndex the chunk index used in key derivation * * @return the AES256-GCM128 key */ std::vector VfsEM_AES256GCM_SHA256::deriveChunkKey(uint32_t chunkIndex) { std::vector chunkSalt{mFileSalt}; chunkSalt.push_back((chunkIndex>>24)&0xFF); chunkSalt.push_back((chunkIndex>>16)&0xFF); chunkSalt.push_back((chunkIndex>>8)&0xFF); chunkSalt.push_back(chunkIndex&0xFF); return bctoolbox::HKDF(chunkSalt, sMasterKey, "EVFS chunk", AES256GCM128::keySize()); } std::vector VfsEM_AES256GCM_SHA256::decryptChunk(const uint32_t chunkIndex, const std::vector &rawChunk) { if (sMasterKey.empty()) { throw EVFS_EXCEPTION<<"No encryption Master key set, cannot decrypt"; } // derive the key : HKDF (fileHeaderSalt || Chunk Index, Master key, "EVFS chunk") std::vector key{deriveChunkKey(chunkIndex)}; // parse the header: tag, IV, encryption Counter std::vector tag(AES256GCM128::tagSize()); std::copy(rawChunk.cbegin(), rawChunk.cbegin()+chunkAuthTagSize, tag.begin()); std::vector IV(rawChunk.cbegin() + chunkAuthTagSize, rawChunk.cbegin() + chunkAuthTagSize + chunkIVSize); std::vector AD{}; // No associated data // extract cipher std::vector cipher(rawChunk.cbegin()+chunkHeaderSize, rawChunk.cend()); // decrypt and auth std::vector plain; if (AEADDecrypt(key, IV, cipher, AD, tag, plain) == false) { throw EVFS_EXCEPTION<<"Authentication failure during chunk decryption"; } // cleaning bctbx_clean(key.data(), key.size()); return plain; } // This module does not reuse any part of its chunk header during encryption // So re-encryption is the same than initial encryption void VfsEM_AES256GCM_SHA256::encryptChunk(const uint32_t chunkIndex, std::vector &rawChunk, const std::vector &plainData) { rawChunk = encryptChunk(chunkIndex, plainData); } std::vector VfsEM_AES256GCM_SHA256::encryptChunk(const uint32_t chunkIndex, const std::vector &plainData) { if (sMasterKey.empty()) { throw EVFS_EXCEPTION<<"No encryption Master key set, cannot encrypt"; } // generate a random IV auto IV = mRNG->randomize(chunkIVSize); // derive the key : HKDF (fileHeaderSalt || Chunk Index, Master key, "EVFS chunk") std::vector key{deriveChunkKey(chunkIndex)}; std::vector AD{}; std::vector tag(AES256GCM128::tagSize()); std::vector rawChunk = AEADEncrypt(key, IV, plainData, AD, tag); // insert header: std::vector chunkHeader(chunkHeaderSize,0); std::copy(tag.cbegin(), tag.cend(), chunkHeader.begin()); std::copy(IV.cbegin(), IV.cend(), chunkHeader.begin()+tag.size()); rawChunk.insert(rawChunk.begin(), chunkHeader.cbegin(), chunkHeader.cend()); // cleaning bctbx_clean(key.data(), key.size()); return rawChunk; } /** * When this function is called, m_fileHeader holds the integrity tag read from file * and sFileHeaderHMACKey holds the derived key for header authentication * Compute the HMAC on the whole rawfileHeader + the module header * Check it match what we have in the m_fileHeader */ bool VfsEM_AES256GCM_SHA256::checkIntegrity(const VfsEncryption &fileContext) { if (sFileHeaderHMACKey.empty()) { throw EVFS_EXCEPTION<<"The AES256GCM128-SHA256 encryption module cannot generate its file header without master key"; } // Only the actual file header is to authenticate, the module file header holds the global salt used to derive the key feed to HMAC authenticating the file header // so it is useless to authenticate it auto tag = HMAC(sFileHeaderHMACKey, fileContext.rawHeaderGet()); return (std::equal(tag.cbegin(), tag.cend(), mFileHeaderIntegrity.cbegin())); } /** * This function exists as static and non static */ size_t VfsEM_AES256GCM_SHA256::moduleFileHeaderSize() noexcept{ return fileHeaderSize; } /** * @return the size in bytes of the chunk header */ size_t VfsEM_AES256GCM_SHA256::getChunkHeaderSize() const noexcept { return chunkHeaderSize; } /** * @return the size in bytes of file header module data */ size_t VfsEM_AES256GCM_SHA256::getModuleFileHeaderSize() const noexcept { return fileHeaderSize; } /** * @return the secret material size */ size_t VfsEM_AES256GCM_SHA256::getSecretMaterialSize() const noexcept { return masterKeySize; } bctoolbox-5.2.0/src/vfs/vfs_encryption_module_aes256gcm_sha256.hh000066400000000000000000000110241434566643100247120ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VFS_ENCRYPTION_MODULE_AES256GCM_SHA256_HH #define BCTBX_VFS_ENCRYPTION_MODULE_AES256GCM_SHA256_HH #include "bctoolbox/vfs_encrypted.hh" #include "vfs_encryption_module.hh" #include "bctoolbox/crypto.hh" #include /*********** The AES256-GCM SHA256 module ************************ * Key derivations: * - file Header HMAC key = HKDF(Mk, fileHeaderSalt, "EVFS file header") * - chunk encryption key = HKDF(Mk, fileHeaderSalt || Chunk Index, "EVFS chunk") * File Header: * - 32 bytes auth tag: HMAC-sha256 on the file header * - 16 bytes salt: random generated at file creation : input of the HKDF keyed by the master key. * Chunk Header: * - Authentication tag : 16 bytes * - IV: 12 bytes. A random updated at each encryption * Chunk encryption: * - AES256-GCM with 128 bit auth tag. No associated Data. * - IV is 12 bytes random : MUST use a random for IV as attacker having access to file system could restore an old version of the file and monitor further writing. So deterministic IV could lead to key/IV reuse. */ namespace bctoolbox { class VfsEM_AES256GCM_SHA256 : public VfsEncryptionModule { private: /** * The local RNG */ std::shared_ptr mRNG; // list it first so it is available in the constructor's init list /** * File header */ std::vector mFileSalt; std::array mFileHeaderIntegrity; /** keys */ std::vector sMasterKey; // used to derive all keys std::vector sFileHeaderHMACKey; // used to feed HMAC integrity check on file header /** * Derive the key from master key for the given chunkIndex: * HKDF(fileSalt || ChunkIndex, master Key, "EVFS chunk") * * @param[in] chunkIndex the chunk index used in key derivation * * @return the AES256-GCM128 key */ std::vector deriveChunkKey(uint32_t chunkIndex); public: /** * This function exists as static and non static */ static size_t moduleFileHeaderSize() noexcept; /** * @return the size in bytes of the chunk header */ size_t getChunkHeaderSize() const noexcept override; /** * @return the size in bytes of file header module data */ size_t getModuleFileHeaderSize() const noexcept override; /** * @return the EncryptionSuite provided by this module */ EncryptionSuite getEncryptionSuite() const noexcept override { return EncryptionSuite::aes256gcm128_sha256; } /** * @return the secret material size */ size_t getSecretMaterialSize() const noexcept override; /** * Decrypt a chunk of data * @param[in] a vector which size shall be chunkHeaderSize + chunkSize holding the raw data read from disk * @return the decrypted data chunk */ std::vector decryptChunk(const uint32_t chunkIndex, const std::vector &rawChunk) override ; void encryptChunk(const uint32_t chunkIndex, std::vector &rawChunk, const std::vector &plainData) override; std::vector encryptChunk(const uint32_t chunkIndex, const std::vector &plainData) override; const std::vector getModuleFileHeader(const VfsEncryption &fileContext) const override ; void setModuleSecretMaterial(const std::vector &secret) override ; /** * Check the integrity over the whole file * @param[in] fileContext a way to access the file content * * @return true if the integrity check successfully passed, false otherwise */ bool checkIntegrity(const VfsEncryption &fileContext) override; /** * constructors */ // At file creation VfsEM_AES256GCM_SHA256(); // Opening an existing file VfsEM_AES256GCM_SHA256(const std::vector &fileHeader); ~VfsEM_AES256GCM_SHA256(); }; } // namespace bctoolbox #endif // BCTBX_VFS_ENCRYPTION_MODULE_AES256GCM_SHA256_HH bctoolbox-5.2.0/src/vfs/vfs_encryption_module_dummy.cc000066400000000000000000000305301434566643100232520ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "vfs_encryption_module_dummy.hh" #include #include #include "bctoolbox/logging.h" #include "bctoolbox/crypto.h" using namespace bctoolbox; // someone does include the evil windef.h so we must undef the min and max macros to be able to use std::min and std::max #undef min #undef max /** * Constant associated to this encryption module */ /** Header in the dummy module holds: * - Integrity tag 8 bytes. The HMACSHA256 of the block content and header (excluding this tag) * - Block Index : 4 bytes. (-> max 4 giga blocks in a file -> more than enough) * - Encryption Counter : 4 bytes counter (increased at each encryption) * Total size : 16 bytes */ static constexpr size_t chunkHeaderSize=16; /** * The dummy module file header holds: * - fixed Random IV : 8 bytes * - Integrity tag 8 bytes. The HMACSHA256 of the file header - (including the begining of the module file header first 8 bytes, excluding this tag) */ static constexpr size_t fileHeaderSize=16; /** * The dummy module secret material is a key used to Xor blocks * - size is 16 bytes */ static constexpr size_t secretMaterialSize=16; static std::string getHex(const std::vector& v) { std::string result; result.reserve(v.size() * 2); // two digits per character static constexpr char hex[] = "0123456789ABCDEF"; for (uint8_t c : v) { result.push_back(hex[c / 16]); result.push_back(hex[c % 16]); } return result; } // chunk index is in chunk 8,9,10,11 uint32_t VfsEncryptionModuleDummy::getChunkIndex(const std::vector &chunk) const { return chunk[8]<<24 | chunk[9]<<16 | chunk[10]<<8 | chunk[11]; } /** * Get global IV. Part of IV common to all chunks */ std::vector VfsEncryptionModuleDummy::globalIV() const { return mFileHeader; } VfsEncryptionModuleDummy::VfsEncryptionModuleDummy() { // this is a constant for the dummy suite to help debug, real module would do otherwise // the fileHeader also holds a integrity part computed on the whole fileHeader in the get function mFileHeader = std::vector{0xaa, 0x55, 0xbb, 0x44, 0xcc, 0x33, 0xdd, 0x22}; mFileHeaderIntegrity.resize(8); mSecret = std::vector{}; } VfsEncryptionModuleDummy::VfsEncryptionModuleDummy(const std::vector &fileHeader) { mSecret = std::vector{}; mFileHeader.resize(8); mFileHeaderIntegrity.resize(8); if (fileHeader.size() != fileHeaderSize) { throw EVFS_EXCEPTION<<"The dummy encryption module expect a fileHeader of size "< VfsEncryptionModuleDummy::getModuleFileHeader(const VfsEncryption &fileContext) const { // Update the integrity on fileHeader auto header = fileContext.rawHeaderGet(); // append the part of the module file header we want to authentify auto moduleAuthentifiedPart = globalIV(); header.insert(header.end(), moduleAuthentifiedPart.cbegin(), moduleAuthentifiedPart.cend()); std::vector tag(8); // Compute HMAC keyed with global key bctbx_hmacSha256(mSecret.data(), secretMaterialSize, header.data(), header.size(), 8, // get 8 bytes out of the HMAC tag.data()); // Append the actual file header value to the tag tag.insert(tag.end(), mFileHeader.cbegin(), mFileHeader.cend()); BCTBX_SLOGD<<"get Module file header returns "< &secret) { if (secret.size() != secretMaterialSize) { throw EVFS_EXCEPTION<<"The dummy encryption module expect a secret material of size "< VfsEncryptionModuleDummy::decryptChunk(const uint32_t chunkIndex, const std::vector &rawChunk) { // First check the integrity of the block. In the dummy module, integrity is 8 bytes of HMAC SHA256 keyed with the master key std::vector computedIntegrity = chunkIntegrityTag(rawChunk); if (!std::equal(computedIntegrity.cbegin(), computedIntegrity.cend(), rawChunk.cbegin())) { throw EVFS_EXCEPTION<<"Integrity check failure while decrypting"; } // Check the given chunk index is matching the one found in block - avoid attacker moving blocks in the file if (chunkIndex != getChunkIndex(rawChunk)) { throw EVFS_EXCEPTION<<"Integrity check: unmatching chunk index"; } std::vector plainData(rawChunk.cbegin()+chunkHeaderSize, rawChunk.cend()); // The dummy decryption is a simple XOR on 16 bytes blocks with fileHeaderMaterial(8 bytes)||chunkHeaderMaterial(8 bytes) // The 16 bytes result is then xor with the secret material std::vector XORkey(globalIV()); // Xor key is file header material XORkey.insert(XORkey.end(), rawChunk.cbegin()+8, rawChunk.cbegin()+chunkHeaderSize); // and chunkHeaderMaterial std::transform(XORkey.begin(), XORkey.end(), mSecret.cbegin(), XORkey.begin(), std::bit_xor()); BCTBX_SLOGD<<"decryptChunk :"<()); } BCTBX_SLOGD<<"decryptChunk :"< &rawChunk, const std::vector &plainData) { BCTBX_SLOGD<<"encryptChunk re :"< computedIntegrity = chunkIntegrityTag(rawChunk); if (!std::equal(computedIntegrity.cbegin(), computedIntegrity.cend(), rawChunk.cbegin())) { throw EVFS_EXCEPTION<<"Integrity check failure while re-encrypting chunk"; } // Check the given chunk index is matching the one found in block - avoid attacker moving blocks in the file if (chunkIndex != getChunkIndex(rawChunk)) { throw EVFS_EXCEPTION<<"Integrity check: unmatching chunk index"; } // Increase the encryption count uint32_t encryptionCount = rawChunk[12]<<24 | rawChunk[13]<<16 | rawChunk[14]<<8 | rawChunk[15]; encryptionCount++; rawChunk[12] = (encryptionCount>>24)&0xFF; rawChunk[13] = (encryptionCount>>16)&0xFF; rawChunk[14] = (encryptionCount>>8)&0xFF; rawChunk[15] = (encryptionCount&0xFF); // resize encrypted buffer rawChunk.resize(chunkHeaderSize+plainData.size()); // The dummy encryption is a simple XOR on 16 bytes blocks with fileHeaderMaterial(8 bytes)||chunkHeaderMaterial(8 bytes, the part after the integrity tag) // The 16 bytes result is then xor with the secret material std::vector XORkey(globalIV()); // Xor key is file header material XORkey.insert(XORkey.end(), rawChunk.cbegin()+8, rawChunk.cbegin()+chunkHeaderSize); // and chunkHeaderMaterial std::transform(XORkey.begin(), XORkey.end(), mSecret.cbegin(), XORkey.begin(), std::bit_xor()); // Xor it all, 16 bytes at a time for (size_t i=0; i()); } // Update integrity computedIntegrity = chunkIntegrityTag(rawChunk); std::copy(computedIntegrity.cbegin(), computedIntegrity.cend(), rawChunk.begin()); BCTBX_SLOGD<<" out cipher: "< VfsEncryptionModuleDummy::encryptChunk(const uint32_t chunkIndex, const std::vector &plainData) { BCTBX_SLOGD<<"encryptChunk new :"< rawChunk(chunkHeaderSize+plainData.size(), 0); // set in the chunk Index rawChunk[8] = (chunkIndex>>24)&0xFF; rawChunk[9] = (chunkIndex>>16)&0xFF; rawChunk[10] = (chunkIndex>>8)&0xFF; rawChunk[11] = (chunkIndex&0xFF); // rawChunk 12 to 15 is the encryptionCount, 0 is fine // The dummy encryption is a simple XOR on 16 bytes blocks with fileHeaderMaterial(8 bytes)||chunkHeaderMaterial(8 bytes, the part after the integrity tag) // The 16 bytes result is then xor with the secret material std::vector XORkey(globalIV()); // Xor key is file header material XORkey.insert(XORkey.end(), rawChunk.cbegin()+8, rawChunk.cbegin()+chunkHeaderSize); // and chunkHeaderMaterial std::transform(XORkey.begin(), XORkey.end(), mSecret.cbegin(), XORkey.begin(), std::bit_xor()); // Xor it all, 16 bytes at a time for (size_t i=0; i()); } // Update integrity auto computedIntegrity = chunkIntegrityTag(rawChunk); std::copy(computedIntegrity.cbegin(), computedIntegrity.cend(), rawChunk.begin()); BCTBX_SLOGD<<" cipher: "< tag(8); // Compute HMAC keyed with global key bctbx_hmacSha256(mSecret.data(), secretMaterialSize, header.data(), header.size(), 8, // get 8 bytes out of the HMAC tag.data()); BCTBX_SLOGD<<"check integrity compute "< VfsEncryptionModuleDummy::chunkIntegrityTag(const std::vector &chunk) const { std::vector tag(8); bctbx_hmacSha256(mSecret.data(), secretMaterialSize, chunk.data()+8, // compute integrity on the whole block (header included) but skip the integrity tag (8 first bytes) chunk.size()-8, 8, // get 8 bytes out of the HMAC tag.data()); return tag; } /** * @return the size in bytes of file header module data */ size_t VfsEncryptionModuleDummy::moduleFileHeaderSize() noexcept{ return fileHeaderSize; } /** * @return the size in bytes of the chunk header */ size_t VfsEncryptionModuleDummy::getChunkHeaderSize() const noexcept { return chunkHeaderSize; } /** * @return the size in bytes of file header module data */ size_t VfsEncryptionModuleDummy::getModuleFileHeaderSize() const noexcept { return fileHeaderSize; } /** * @return the secret material size */ size_t VfsEncryptionModuleDummy::getSecretMaterialSize() const noexcept { return secretMaterialSize; } bctoolbox-5.2.0/src/vfs/vfs_encryption_module_dummy.hh000066400000000000000000000067361434566643100232770ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 BCTBX_VFS_ENCRYPTION_MODULE_DUMMY_HH #define BCTBX_VFS_ENCRYPTION_MODULE_DUMMY_HH #include "bctoolbox/vfs_encrypted.hh" #include "vfs_encryption_module.hh" namespace bctoolbox { class VfsEncryptionModuleDummy : public VfsEncryptionModule { private: /** * Store the file header and secret */ std::vector mFileHeader; std::vector mFileHeaderIntegrity; std::vector mSecret; /** * Compute the integrity tag in the given chunk */ std::vector chunkIntegrityTag(const std::vector &chunk) const; /** * Get the chunk index from the given chunk */ uint32_t getChunkIndex(const std::vector &chunk) const; /** * Get global IV. Part of IV common to all chunks * The last 8 bytes of the file header */ std::vector globalIV() const; public: /** * @return the size in bytes of file header module data */ static size_t moduleFileHeaderSize() noexcept; /** * @return the size in bytes of the chunk header */ size_t getChunkHeaderSize() const noexcept override; /** * @return the size in bytes of file header module data */ size_t getModuleFileHeaderSize() const noexcept override; /** * @return the EncryptionSuite provided by this module */ EncryptionSuite getEncryptionSuite() const noexcept override { return EncryptionSuite::dummy; } /** * @return the secret material size */ size_t getSecretMaterialSize() const noexcept override; /** * Decrypt a chunk of data * @param[in] a vector which size shall be chunkHeaderSize + chunkSize holding the raw data read from disk * @return the decrypted data chunk */ std::vector decryptChunk(const uint32_t chunkIndex, const std::vector &rawChunk) override ; void encryptChunk(const uint32_t chunkIndex, std::vector &rawChunk, const std::vector &plainData) override; std::vector encryptChunk(const uint32_t chunkIndex, const std::vector &plainData) override; const std::vector getModuleFileHeader(const VfsEncryption &fileContext) const override ; void setModuleSecretMaterial(const std::vector &secret) override ; /** * Check the integrity over the whole file * @param[in] fileContext a way to access the file content * * @return true if the integrity check successfully passed, false otherwise */ bool checkIntegrity(const VfsEncryption &fileContext) override; /** * constructors */ // At file creation VfsEncryptionModuleDummy(); // Opening an existing file VfsEncryptionModuleDummy(const std::vector &moduleFileHeader); ~VfsEncryptionModuleDummy() {}; }; } // namespace bctoolbox #endif // BCTBX_VFS_ENCRYPTION_MODULE_DUMMY bctoolbox-5.2.0/src/vfs/vfs_standard.c000066400000000000000000000162251434566643100177420ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/vfs.h" #include "bctoolbox/port.h" #include "bctoolbox/logging.h" #include #include #include /** * Opens the file with filename fName, associate it to the file handle pointed * by pFile, sets the methods bctbx_io_methods_t to the bcio structure * and initializes the file size. * Sets the error in pErrSvd if an error occurred while opening the file fName. * @param pVfs Pointer to bctx_vfs VFS. * @param fName Absolute path filename. * @param openFlags Flags to use when opening the file. * @return BCTBX_VFS_ERROR if an error occurs, BCTBX_VFS_OK otherwise. */ static int bcOpen(bctbx_vfs_t *pVfs, bctbx_vfs_file_t *pFile, const char *fName, int openFlags); /* User data for the standard vfs */ typedef struct bctbx_vfs_standard_t bctbx_vfs_standard_t; struct bctbx_vfs_standard_t { int fd; /* File descriptor */ }; bctbx_vfs_t bcStandardVfs = { "bctbx_vfs", /* vfsName */ bcOpen, /*xOpen */ }; /** * Closes file by closing the associated file descriptor. * Sets the error errno in the argument pErrSrvd after allocating it * if an error occurrred. * @param pFile bctbx_vfs_file_t File handle pointer. * @return BCTBX_VFS_OK if successful, BCTBX_VFS_ERROR otherwise. */ static int bcClose(bctbx_vfs_file_t *pFile) { int ret; if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; ret = close(ctx->fd); if (!ret) { ret = BCTBX_VFS_OK; } else { ret = -errno; } bctbx_free(pFile->pUserData); return ret; } /** * Simply sync the file contents given through the file handle * to the persistent media. * @param pFile File handle pointer. * @return BCTBX_VFS_OK on success, BCTBX_VFS_ERROR otherwise */ static int bcSync(bctbx_vfs_file_t *pFile) { if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; #if _WIN32 int ret; ret = FlushFileBuffers((HANDLE)_get_osfhandle(ctx->fd)); return (ret!=0 ? BCTBX_VFS_OK : BCTBX_VFS_ERROR); #else int rc = fsync(ctx->fd); return (rc==0 ? BCTBX_VFS_OK : BCTBX_VFS_ERROR); #endif } /** * Read count bytes from the open file given by pFile, starting at offset. * Sets the error errno in the argument pErrSrvd after allocating it * if an error occurrred. * @param pFile File handle pointer. * @param buf buffer to write the read bytes to. * @param count number of bytes to read * @param offset file offset where to start reading * @return -errno if erroneous read, number of bytes read (count) on success, * if the error was something else BCTBX_VFS_ERROR otherwise */ static ssize_t bcRead(bctbx_vfs_file_t *pFile, void *buf, size_t count, off_t offset) { ssize_t nRead; /* Return value from read() */ if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; if (lseek(ctx->fd, offset, SEEK_SET) < 0) { if (errno) return -errno; } else { nRead = bctbx_read(ctx->fd, buf, count); /* Error while reading */ if (nRead < 0) { if (errno) return -errno; } return nRead; } return BCTBX_VFS_ERROR; } /** * Writes directly to the open file given through the pFile argument. * Sets the error errno in the argument pErrSrvd after allocating it * if an error occurrred. * @param pFile bctbx_vfs_file_t File handle pointer. * @param buf Buffer containing data to write * @param count Size of data to write in bytes * @param offset File offset where to write to * @return number of bytes written (can be 0), negative value errno if an error occurred. */ static ssize_t bcWrite(bctbx_vfs_file_t *pFile, const void *buf, size_t count, off_t offset) { ssize_t nWrite = 0; /* Return value from write() */ if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; if ((lseek(ctx->fd, offset, SEEK_SET)) < 0) { if (errno) return -errno; } else { nWrite = bctbx_write(ctx->fd, buf, count); if (nWrite > 0) return nWrite; else if (nWrite <= 0) { if (errno) return -errno; return 0; } } return BCTBX_VFS_ERROR; } /** * Returns the file size associated with the file handle pFile. * @param pFile File handle pointer. * @return -errno if an error occurred, file size otherwise (can be 0). */ static int64_t bcFileSize(bctbx_vfs_file_t *pFile) { int rc; /* Return code from fstat() call */ struct stat sStat; /* Output of fstat() call */ if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; rc = fstat(ctx->fd, &sStat); if (rc != 0) { return -errno; } return sStat.st_size; } /* ** Truncate a file * @param pFile File handle pointer. * @param new_size Extends the file with null bytes if it is superiori to the file's size * truncates the file otherwise. * @return -errno if an error occurred, 0 otherwise. */ static int bcTruncate(bctbx_vfs_file_t *pFile, int64_t new_size){ int ret; if (pFile==NULL || pFile->pUserData==NULL) return BCTBX_VFS_ERROR; bctbx_vfs_standard_t *ctx = (bctbx_vfs_standard_t *)pFile->pUserData; #if _WIN32 ret = _chsize(ctx->fd, (long)new_size); #else ret = ftruncate(ctx->fd, new_size); #endif if (ret < 0) { return -errno; } return 0; } static const bctbx_io_methods_t bcio = { bcClose, /* pFuncClose */ bcRead, /* pFuncRead */ bcWrite, /* pFuncWrite */ bcTruncate, /* pFuncTruncate */ bcFileSize, /* pFuncFileSize */ bcSync, NULL, /* use the generic implementation of getnxt line */ NULL /* pFuncIsEncrypted -> no function so we will return false */ }; static int bcOpen(bctbx_vfs_t *pVfs, bctbx_vfs_file_t *pFile, const char *fName, int openFlags) { if (pFile == NULL || fName == NULL) { return BCTBX_VFS_ERROR; } #ifdef _WIN32 openFlags |= O_BINARY; #endif /* Create the userData structure */ bctbx_vfs_standard_t *userData = (bctbx_vfs_standard_t *)bctbx_malloc(sizeof(bctbx_vfs_standard_t)); userData->fd = open(fName, openFlags, S_IRUSR | S_IWUSR); if (userData->fd == -1) { bctbx_free(userData); return -errno; } pFile->pMethods = &bcio; pFile->pUserData = (void *)userData; return BCTBX_VFS_OK; } bctoolbox-5.2.0/tester/000077500000000000000000000000001434566643100150335ustar00rootroot00000000000000bctoolbox-5.2.0/tester/CMakeLists.txt000066400000000000000000000045511434566643100176000ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2020 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. # ############################################################################ if(ENABLE_SHARED) set(PROJECT_LIBS bctoolbox bctoolbox-tester) else() set(PROJECT_LIBS bctoolbox-static bctoolbox-tester-static) endif() if(ENABLE_TESTS AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") set(TESTER_SOURCES bctoolbox_tester.c bctoolbox_tester.h containers.cc port.c parser.c param_string.c vfs.c ) if(MBEDTLS_FOUND OR POLARSSL_FOUND) list(APPEND TESTER_SOURCES crypto.cc encrypted_vfs.cc) endif() if(APPLE) list(APPEND TESTER_SOURCES ios_utils.cc) endif() string(REPLACE ";" " " LINK_FLAGS_STR "${LINK_FLAGS}") add_executable(bctoolbox_tester_exe ${TESTER_SOURCES}) if(NOT "${LINK_FLAGS_STR}" STREQUAL "") set_target_properties(bctoolbox_tester_exe PROPERTIES LINK_FLAGS "${LINK_FLAGS_STR}") endif() set_target_properties(bctoolbox_tester_exe PROPERTIES OUTPUT_NAME bctoolbox_tester) target_link_libraries(bctoolbox_tester_exe PRIVATE ${PROJECT_LIBS}) if(MBEDTLS_FOUND) target_link_libraries(bctoolbox_tester_exe PRIVATE ${MBEDTLS_TARGETS}) endif() if(POLARSSL_FOUND) target_link_libraries(bctoolbox_tester_exe PRIVATE ${POLARSSL_LIBRARIES}) endif() if(DECAF_FOUND) target_link_libraries(bctoolbox_tester_exe PRIVATE ${DECAF_TARGETNAME}) endif() set_target_properties(bctoolbox_tester_exe PROPERTIES XCODE_ATTRIBUTE_WARNING_CFLAGS "") add_test(NAME bctoolbox_tester COMMAND bctoolbox_tester --verbose) endif() bctoolbox-5.2.0/tester/bctoolbox_tester.c000066400000000000000000000070521434566643100205640ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bctoolbox/logging.h" #include "bctoolbox_tester.h" static const char *log_domain = "bctoolbox-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,lev, fmt, args); } void bctoolbox_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args)) { bc_tester_init(log_handler,BCTBX_LOG_MESSAGE, BCTBX_LOG_ERROR, NULL); bc_tester_add_suite(&containers_test_suite); bc_tester_add_suite(&utils_test_suite); #if (HAVE_MBEDTLS | HAVE_POLARSSL) bc_tester_add_suite(&crypto_test_suite); bc_tester_add_suite(&encrypted_vfs_test_suite); #endif bc_tester_add_suite(&parser_test_suite); #ifdef __APPLE__ bc_tester_add_suite(&ios_utils_test_suite); #endif bc_tester_add_suite(¶m_string_test_suite); bc_tester_add_suite(&vfs_test_suite); } void bctoolbox_tester_uninit(void) { bc_tester_uninit(); } void bctoolbox_tester_before_each() { } int bctoolbox_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: bctbx_free(dir); bctbx_free(base); return res; } #if !defined(__ANDROID__) && !(defined(BCTBX_WINDOWS_PHONE) || defined(BCTBX_WINDOWS_UNIVERSAL)) static const char* bctoolbox_helper = "\t\t\t--verbose\n" "\t\t\t--silent\n" "\t\t\t--log-file \n"; int main (int argc, char *argv[]) { int i; int ret; bctbx_init_logger(TRUE); bctoolbox_tester_init(NULL); for(i=1;i0) { i += ret - 1; continue; } else if (ret<0) { bc_tester_helper(argv[0], bctoolbox_helper); } return ret; } } ret = bc_tester_start(argv[0]); bctoolbox_tester_uninit(); bctbx_uninit_logger(); return ret; } #endif bctoolbox-5.2.0/tester/bctoolbox_tester.h000066400000000000000000000027231434566643100205710ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 _BCTOOLBOX_TESTER_H #define _BCTOOLBOX_TESTER_H #include "bctoolbox/logging.h" #include "bctoolbox/tester.h" #ifdef __cplusplus #define SLOGD BCTBX_SLOGD("bctoolbox-tester") #define SLOGI BCTBX_SLOGI("bctoolbox-tester") #define SLOGW BCTBX_SLOGW("bctoolbox-tester") #define SLOGE BCTBX_SLOGE("bctoolbox-tester") extern "C" { #endif extern test_suite_t containers_test_suite; extern test_suite_t utils_test_suite; extern test_suite_t crypto_test_suite; extern test_suite_t parser_test_suite; extern test_suite_t ios_utils_test_suite; extern test_suite_t encrypted_vfs_test_suite; extern test_suite_t param_string_test_suite; extern test_suite_t vfs_test_suite; #ifdef __cplusplus }; #endif #endif /* _BCTOOLBOX_TESTER_H */ bctoolbox-5.2.0/tester/containers.cc000066400000000000000000000147471434566643100175240ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/map.h" #include "bctoolbox/list.h" static void multimap_insert(void) { bctbx_map_t *mmap = bctbx_mmap_ullong_new(); bctbx_list_t *head = NULL, *ref = NULL; bctbx_iterator_t *it,*end; long i=0; int N = 100; for(i=0;i. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "bctoolbox_tester.h" #include "bctoolbox/crypto.h" #include "bctoolbox/crypto.hh" #include "bctoolbox/exception.hh" #ifdef HAVE_MBEDTLS /* used to cross test ECDH25519 */ #include "mbedtls/ecdh.h" #endif /* HAVE_MBEDTLS */ #include using namespace bctoolbox; static void DHM(void) { int i; bctbx_DHMContext_t *alice,*bob; bctbx_rng_context_t *RNG; uint8_t availableAlgos[2]={BCTBX_DHM_2048, BCTBX_DHM_3072}; uint8_t availableAlgosNb=2; /* Init the RNG */ RNG = bctbx_rng_context_new(); /* Get all the available DH algos */ //availableAlgosNb=bctbx_getDHMAvailableAlgos(availableAlgos); for (i=0; ipeer = (uint8_t *)malloc(keySize*sizeof(uint8_t)); memcpy(alice->peer,bob->self,keySize); bob->peer = (uint8_t *)malloc(keySize*sizeof(uint8_t)); memcpy(bob->peer,alice->self,keySize); /* compute shared secrets */ bctbx_DHMComputeSecret(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG); bctbx_DHMComputeSecret(bob, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG); /* compare the secrets */ BC_ASSERT_TRUE(memcmp(alice->key,bob->key,keySize)==0); /* clear contexts */ bctbx_DestroyDHMContext(alice); bctbx_DestroyDHMContext(bob); } /* clear contexts */ bctbx_rng_context_free(RNG); } static void ECDH_exchange(bctbx_ECDHContext_t *alice, bctbx_ECDHContext_t *bob) { /* exchange public keys */ bctbx_ECDHSetPeerPublicKey(alice, bob->selfPublic, alice->pointCoordinateLength); bctbx_ECDHSetPeerPublicKey(bob, alice->selfPublic, bob->pointCoordinateLength); /* compute shared secrets */ bctbx_ECDHComputeSecret(alice, NULL, NULL); bctbx_ECDHComputeSecret(bob, NULL, NULL); /* compare the secrets */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, bob->sharedSecret, alice->pointCoordinateLength)==0); } static void ECDH(void) { if (!bctbx_crypto_have_ecc()) { bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox"); return; } /* Patterns */ uint8_t ECDHpattern_X25519_alicePrivate[] = {0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a}; uint8_t ECDHpattern_X25519_alicePublic[] = {0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a}; uint8_t ECDHpattern_X25519_bobPrivate[] = {0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb}; uint8_t ECDHpattern_X25519_bobPublic[] = {0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f}; uint8_t ECDHpattern_X25519_sharedSecret[] = {0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42}; uint8_t alicePublic_libSignalPattern[] = { 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28}; uint8_t alicePrivate_libSignalPattern[] = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59}; uint8_t bobPublic_libSignalPattern[] = { 0x65, 0x36, 0x14, 0x99, 0x3d, 0x2b, 0x15, 0xee, 0x9e, 0x5f, 0xd3, 0xd8, 0x6c, 0xe7, 0x19, 0xef, 0x4e, 0xc1, 0xda, 0xae, 0x18, 0x86, 0xa8, 0x7b, 0x3f, 0x5f, 0xa9, 0x56, 0x5a, 0x27, 0xa2, 0x2f}; uint8_t bobPrivate_libSignalPattern[] = { 0xb0, 0x3b, 0x34, 0xc3, 0x3a, 0x1c, 0x44, 0xf2, 0x25, 0xb6, 0x62, 0xd2, 0xbf, 0x48, 0x59, 0xb8, 0x13, 0x54, 0x11, 0xfa, 0x7b, 0x03, 0x86, 0xd4, 0x5f, 0xb7, 0x5d, 0xc5, 0xb9, 0x1b, 0x44, 0x66}; uint8_t shared_libSignalPattern[] = { 0x32, 0x5f, 0x23, 0x93, 0x28, 0x94, 0x1c, 0xed, 0x6e, 0x67, 0x3b, 0x86, 0xba, 0x41, 0x01, 0x74, 0x48, 0xe9, 0x9b, 0x64, 0x9a, 0x9c, 0x38, 0x06, 0xc1, 0xdd, 0x7c, 0xa4, 0xc4, 0x77, 0xe6, 0x29}; uint8_t ECDHpattern_X448_alicePrivate[] = {0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4, 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5, 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b}; uint8_t ECDHpattern_X448_alicePublic[] = {0x9b, 0x08, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x07, 0x4a, 0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22, 0xc5, 0xd9, 0xbb, 0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0x0c, 0x5b, 0x12, 0xda, 0x88, 0x12, 0x0d, 0x53, 0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0}; uint8_t ECDHpattern_X448_bobPrivate[] = {0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, 0x99, 0x0b, 0x29, 0x44, 0x70, 0xcb, 0xa3, 0x39, 0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, 0x8f, 0xad, 0x0d, 0x1d, 0x69, 0x27, 0xc1, 0x20, 0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0x0d, 0x3e, 0x21, 0x37, 0x4c, 0x9c, 0x92, 0x1b, 0x09, 0xd1, 0xb0, 0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d}; uint8_t ECDHpattern_X448_bobPublic[] = {0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0x0b, 0x59, 0x9b, 0x6f, 0xec, 0xcf, 0x6d, 0xa4, 0x62, 0x71, 0x07, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30, 0x27, 0xd8, 0xb9, 0x72, 0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x06, 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae, 0x07, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x09}; uint8_t ECDHpattern_X448_sharedSecret[] = {0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1, 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56, 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a, 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d}; int i; bctbx_ECDHContext_t *alice,*bob; bctbx_rng_context_t *RNG; uint8_t availableAlgos[2]={BCTBX_ECDH_X25519, BCTBX_ECDH_X448}; uint8_t availableAlgosNb=2; /********************************************************************/ /*** Do a random generation and exchange with all available algos ***/ /********************************************************************/ /* Init the RNG */ RNG = bctbx_rng_context_new(); for (i=0; isharedSecret, ECDHpattern_X25519_sharedSecret, 32)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); bctbx_DestroyECDHContext(bob); /********************************************************************/ /*** Run an key derivation and exchange using patterns from RFC7748 */ /********************************************************************/ /*** Run it on the X25519 patterns ***/ /* set contexts */ alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); bob = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); /* Set private and derive the public value */ bctbx_ECDHSetSecretKey(alice, ECDHpattern_X25519_alicePrivate, 32); bctbx_ECDHDerivePublicKey(alice); bctbx_ECDHSetSecretKey(bob, ECDHpattern_X25519_bobPrivate, 32); bctbx_ECDHDerivePublicKey(bob); /* check the public value according to RFC7748 patterns */ BC_ASSERT_TRUE(memcmp(alice->selfPublic, ECDHpattern_X25519_alicePublic, 32)==0); BC_ASSERT_TRUE(memcmp(bob->selfPublic, ECDHpattern_X25519_bobPublic, 32)==0); /* Perform the key exchange and compute shared secret, it will check shared secrets are matching */ ECDH_exchange(alice, bob); /* check shared secret according to RFC7748 patterns */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X25519_sharedSecret, 32)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); bctbx_DestroyECDHContext(bob); /**********************************************************************/ /*** Run an key derivation and exchange using patterns from libsignal */ /**********************************************************************/ /* Do another one using pattern retrieved from libsignal tests */ /* set contexts */ alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); bob = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); /* Set private and derive the public value */ bctbx_ECDHSetSecretKey(alice, alicePrivate_libSignalPattern, 32); bctbx_ECDHDerivePublicKey(alice); bctbx_ECDHSetSecretKey(bob, bobPrivate_libSignalPattern, 32); bctbx_ECDHDerivePublicKey(bob); /* check the public value according to libsignal patterns */ BC_ASSERT_TRUE(memcmp(alice->selfPublic, alicePublic_libSignalPattern, 32)==0); BC_ASSERT_TRUE(memcmp(bob->selfPublic, bobPublic_libSignalPattern, 32)==0); /* Perform the key exchange and compute shared secret, it will check shared secrets are matching */ ECDH_exchange(alice, bob); /* check shared secret according to libsignal patterns */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, shared_libSignalPattern, 32)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); bctbx_DestroyECDHContext(bob); /********************************************************************/ /*** Run an exchange using patterns from RFC7748 */ /********************************************************************/ /*** Run it on the X448 patterns ***/ /* set contexts */ alice = bctbx_CreateECDHContext(BCTBX_ECDH_X448); bob = bctbx_CreateECDHContext(BCTBX_ECDH_X448); /* Set private and derive the public value */ bctbx_ECDHSetSecretKey(alice, ECDHpattern_X448_alicePrivate, 56); bctbx_ECDHSetSelfPublicKey(alice, ECDHpattern_X448_alicePublic, 56); bctbx_ECDHSetSecretKey(bob, ECDHpattern_X448_bobPrivate, 56); bctbx_ECDHSetSelfPublicKey(bob, ECDHpattern_X448_bobPublic, 56); /* Perform the key exchange and compute shared secret, it will check shared secrets are matching */ ECDH_exchange(alice, bob); /* check shared secret according to RFC7748 patterns */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X448_sharedSecret, 56)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); bctbx_DestroyECDHContext(bob); /********************************************************************/ /*** Run an key derivation and exchange using patterns from RFC7748 */ /********************************************************************/ /*** Run it on the X448 patterns ***/ /* set contexts */ alice = bctbx_CreateECDHContext(BCTBX_ECDH_X448); bob = bctbx_CreateECDHContext(BCTBX_ECDH_X448); /* Set private and derive the public value */ bctbx_ECDHSetSecretKey(alice, ECDHpattern_X448_alicePrivate, 56); bctbx_ECDHDerivePublicKey(alice); bctbx_ECDHSetSecretKey(bob, ECDHpattern_X448_bobPrivate, 56); bctbx_ECDHDerivePublicKey(bob); /* check the public value according to RFC7748 patterns */ BC_ASSERT_TRUE(memcmp(alice->selfPublic, ECDHpattern_X448_alicePublic, 56)==0); BC_ASSERT_TRUE(memcmp(bob->selfPublic, ECDHpattern_X448_bobPublic, 56)==0); /* Perform the key exchange and compute shared secret, it will check shared secrets are matching */ ECDH_exchange(alice, bob); /* check shared secret according to RFC7748 patterns */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, ECDHpattern_X448_sharedSecret, 56)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); bctbx_DestroyECDHContext(bob); } /* just check compatibility of ECDH exchange between mbedtls implementation and decaf one */ /* mbedtls works with all buffer in big endian, while rfc 7748 specify little endian encoding, so all buffer in or out of mbedtls_mpi_read/write must be reversed */ static void ECDH25519compat(void) { #ifdef HAVE_MBEDTLS int i; bctbx_ECDHContext_t *alice=NULL; bctbx_rng_context_t *RNG; mbedtls_ecdh_context *bob=NULL; uint8_t tmpBuffer[32]; uint8_t bobPublic[32]; uint8_t bobShared[32]; if (!bctbx_crypto_have_ecc()) { bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox"); return; } /* Init the RNG */ RNG = bctbx_rng_context_new(); /* Create Alice and Bob contexts */ alice = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); bob=(mbedtls_ecdh_context *)bctbx_malloc(sizeof(mbedtls_ecdh_context)); mbedtls_ecdh_init(bob); mbedtls_ecp_group_load(&(bob->grp), MBEDTLS_ECP_DP_CURVE25519 ); /* Generate keys pairs */ bctbx_ECDHCreateKeyPair(alice, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, RNG); mbedtls_ecdh_gen_public(&(bob->grp), &(bob->d), &(bob->Q), (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG); mbedtls_mpi_write_binary(&(bob->Q.X), tmpBuffer, 32 ); /* tmpBuffer is in big endian, but we need it in little endian, reverse it */ for (i=0; i<32; i++) { bobPublic[i]=tmpBuffer[31-i]; } /* exchange public keys */ alice->peerPublic = (uint8_t *)malloc(alice->pointCoordinateLength*sizeof(uint8_t)); memcpy(alice->peerPublic, bobPublic, alice->pointCoordinateLength); /* compute shared secrets */ bctbx_ECDHComputeSecret(alice, NULL, NULL); mbedtls_mpi_lset(&(bob->Qp.Z), 1); /* alice->selfPublic is in little endian, but mbedtls expect it in big, reverse it */ for (i=0; i<32; i++) { tmpBuffer[i]=alice->selfPublic[31-i]; } mbedtls_mpi_read_binary(&(bob->Qp.X), tmpBuffer, 32); /* generate shared secret */ mbedtls_ecdh_compute_shared(&(bob->grp), &(bob->z), &(bob->Qp), &(bob->d), (int (*)(void *, unsigned char *, size_t))bctbx_rng_get, RNG); /* copy it in the output buffer */ mbedtls_mpi_write_binary(&(bob->z), tmpBuffer, 32); /* reverse it as we need it in little endian */ for (i=0; i<32; i++) { bobShared[i]=tmpBuffer[31-i]; } /* compare the secrets */ BC_ASSERT_TRUE(memcmp(alice->sharedSecret, bobShared, alice->pointCoordinateLength)==0); /* clear contexts */ bctbx_DestroyECDHContext(alice); mbedtls_ecdh_free(bob); free(bob); /* clear contexts */ bctbx_rng_context_free(RNG); #else /* HAVE_MBEDTLS */ bctbx_warning("test skipped as we don't have mbedtls in bctoolbox"); #endif /* HAVE_MBEDTLS */ } static char const *importantMessage1 = "The most obvious mechanical phenomenon in electrical and magnetical experiments is the mutual action by which bodies in certain states set each other in motion while still at a sensible distance from each other. The first step, therefore, in reducing these phenomena into scientific form, is to ascertain the magnitude and direction of the force acting between the bodies, and when it is found that this force depends in a certain way upon the relative position of the bodies and on their electric or magnetic condition, it seems at first sight natural to explain the facts by assuming the existence of something either at rest or in motion in each body, constituting its electric or magnetic state, and capable of acting at a distance according to mathematical laws.In this way mathematical theories of statical electricity, of magnetism, of the mechanical action between conductors carrying currents, and of the induction of currents have been formed. In these theories the force acting between the two bodies is treated with reference only to the condition of the bodies and their relative position, and without any express consideration of the surrounding medium. These theories assume, more or less explicitly, the existence of substances the particles of which have the property of acting on one another at a distance by attraction or repulsion. The most complete development of a theory of this kind is that of M.W. Weber[1], who has made the same theory include electrostatic and electromagnetic phenomena. In doing so, however, he has found it necessary to assume that the force between two particles depends on their relative velocity, as well as on their distance. This theory, as developed by MM. W. Weber and C. Neumann[2], is exceedingly ingenious, and wonderfully comprehensive in its application to the phenomena of statical electricity, electromagnetic attractions, induction of current and diamagnetic phenomena; and it comes to us with the more authority, as it has served to guide the speculations of one who has made so great an advance in the practical part of electric science, both by introducing a consistent system of units in electrical measurement, and by actually determining electrical quantities with an accuracy hitherto unknown."; static char const *importantMessage2 = " The mechanical difficulties, however, which are involved in the assumption of particles acting at a distance with forces which depend on their velocities are such as to prevent me from considering this theory as an ultimate one though it may have been, and may yet be useful in leading to the coordination of phenomena. I have therefore preferred to seek an explanation of the fact in another direction, by supposing them to be produced by actions which go on in the surrounding medium as well as in the excited bodies, and endeavouring to explain the action between distant bodies without assuming the existence of forces capable of acting directly at sensible distances."; static void EdDSA(void) { int i; bctbx_EDDSAContext_t *james,*world; bctbx_rng_context_t *RNG; uint8_t availableAlgos[2]={BCTBX_EDDSA_25519, BCTBX_EDDSA_448}; uint8_t availableAlgosNb=2; uint8_t signature[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */ size_t signatureLength = 128; uint8_t context[250]; if (!bctbx_crypto_have_ecc()) { bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox"); return; } /* Init the RNG */ RNG = bctbx_rng_context_new(); for (i=0; ipublicKey, james->pointCoordinateLength); /* world verifies that the important message was signed by james */ BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_SUCCESS, int, "%d"); /* twist the signature to get it wrong and verify again, it shall fail */ signature[0] ^=0xFF; BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_FAILED, int, "%d"); /* twist the context to get it wrong and verify again, it shall fail */ signature[0] ^=0xFF; context[0] ^=0xFF; BC_ASSERT_EQUAL(bctbx_EDDSA_verify(world, (uint8_t *)importantMessage1, strlen(importantMessage1), context, 250, signature, signatureLength), BCTBX_VERIFY_FAILED, int, "%d"); /* cleaning */ bctbx_DestroyEDDSAContext(james); bctbx_DestroyEDDSAContext(world); } bctbx_rng_context_free(RNG); } static void ed25519_to_x25519_keyconversion(void) { uint8_t pattern_ed25519_publicKey[] = {0xA4, 0xBF, 0x35, 0x3D, 0x6C, 0x9D, 0x51, 0xCA, 0x6D, 0x98, 0x88, 0xA6, 0x26, 0x8C, 0xF2, 0xE8, 0xA5, 0xAD, 0x58, 0x97, 0x00, 0x5B, 0x58, 0xCC, 0x46, 0x82, 0xEB, 0x88, 0x21, 0x9A, 0xC0, 0x18}; uint8_t pattern_ed25519_secretKey[] = {0x9E, 0xEE, 0x80, 0x89, 0xA1, 0x47, 0x6E, 0x4B, 0x01, 0x70, 0xE4, 0x74, 0x06, 0xE1, 0xCE, 0xF8, 0x62, 0x53, 0xE1, 0xC2, 0x3C, 0xDD, 0x63, 0x53, 0x8D, 0x2B, 0xF0, 0x3B, 0x52, 0xD9, 0x6C, 0x39}; uint8_t pattern_x25519_publicKey[] = {0x53, 0x97, 0x95, 0x45, 0x1A, 0x04, 0xB5, 0xDD, 0x42, 0xD2, 0x73, 0x32, 0x9C, 0x1A, 0xC9, 0xFE, 0x58, 0x3A, 0x82, 0xF1, 0x82, 0xE8, 0xD7, 0xA5, 0xAD, 0xCB, 0xD0, 0x27, 0x6E, 0x03, 0xD7, 0x70}; bctbx_ECDHContext_t *aliceECDH = bctbx_CreateECDHContext(BCTBX_ECDH_X25519); bctbx_EDDSAContext_t *aliceEDDSA = bctbx_CreateEDDSAContext(BCTBX_EDDSA_25519); if (!bctbx_crypto_have_ecc()) { bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox"); return; } /* Start from ed25519 secret key and derive the public one */ bctbx_EDDSA_setSecretKey(aliceEDDSA, pattern_ed25519_secretKey, 32); bctbx_EDDSADerivePublicKey(aliceEDDSA); BC_ASSERT_TRUE(memcmp(aliceEDDSA->publicKey, pattern_ed25519_publicKey, 32)==0); /* Convert ed25519 private to x25519 and check it derives to the correct public key (do not check direct value of private as some bits are masked at use but not necessarily during conversion) */ bctbx_EDDSA_ECDH_privateKeyConversion(aliceEDDSA, aliceECDH); bctbx_ECDHDerivePublicKey(aliceECDH); BC_ASSERT_TRUE(memcmp(aliceECDH->selfPublic, pattern_x25519_publicKey, 32)==0); /* Convert directly ed25519 public to x25519 and check we stick to pattern */ bctbx_EDDSA_ECDH_publicKeyConversion(aliceEDDSA, aliceECDH, BCTBX_ECDH_ISPEER);/* store it in peerPublic just for this test purpose */ BC_ASSERT_TRUE(memcmp(aliceECDH->peerPublic, pattern_x25519_publicKey, 32)==0); /* cleaning */ bctbx_DestroyEDDSAContext(aliceEDDSA); bctbx_DestroyECDHContext(aliceECDH); } static void sign_and_key_exchange(void) { int i; bctbx_rng_context_t *RNG; bctbx_ECDHContext_t *aliceECDH = NULL; bctbx_EDDSAContext_t *aliceEDDSA = NULL; bctbx_ECDHContext_t *bobECDH = NULL; bctbx_EDDSAContext_t *bobEDDSA = NULL; uint8_t availableAlgosEDDSA[2]={BCTBX_EDDSA_25519, BCTBX_EDDSA_448}; uint8_t availableAlgosECDH[2]={BCTBX_ECDH_X25519, BCTBX_ECDH_X448}; uint8_t availableAlgosNb=2; uint8_t signature1[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */ size_t signatureLength1 = 128; uint8_t signature2[128]; /* buffer to store the signature, must be at least twice the size of the longer point coordinate (57*2) */ size_t signatureLength2 = 128; uint8_t tmpKeyBuffer[64]; /* hold the EDDSA public key while swapping them between bob and alice */ uint8_t context1[250]; uint8_t context2[250]; if (!bctbx_crypto_have_ecc()) { bctbx_warning("test skipped as we don't have Elliptic Curve Cryptography in bctoolbox"); return; } /* Init the RNG */ RNG = bctbx_rng_context_new(); for (i=0; ipublicKey, bobEDDSA->pointCoordinateLength); bctbx_EDDSA_setPublicKey(bobEDDSA, aliceEDDSA->publicKey, aliceEDDSA->pointCoordinateLength); bctbx_EDDSA_setPublicKey(aliceEDDSA, tmpKeyBuffer, bobEDDSA->pointCoordinateLength); /* convert peer public key to ECDH format, peer public keys are now in the EDDSA context */ bctbx_EDDSA_ECDH_publicKeyConversion(aliceEDDSA, aliceECDH, BCTBX_ECDH_ISPEER); bctbx_EDDSA_ECDH_publicKeyConversion(bobEDDSA, bobECDH, BCTBX_ECDH_ISPEER); /* Verify signed messages */ BC_ASSERT_EQUAL(bctbx_EDDSA_verify(bobEDDSA, (uint8_t *)importantMessage1, strlen(importantMessage1), context1, 250, signature1, signatureLength1), BCTBX_VERIFY_SUCCESS, int, "%d"); BC_ASSERT_EQUAL(bctbx_EDDSA_verify(aliceEDDSA, (uint8_t *)importantMessage2, strlen(importantMessage2), context2, 250, signature2, signatureLength2), BCTBX_VERIFY_SUCCESS, int, "%d"); /* Compute shared secret and compare them */ bctbx_ECDHComputeSecret(aliceECDH, NULL, NULL); bctbx_ECDHComputeSecret(bobECDH, NULL, NULL); /* compare the secrets */ BC_ASSERT_TRUE(memcmp(aliceECDH->sharedSecret, bobECDH->sharedSecret, aliceECDH->pointCoordinateLength)==0); /* reset signatureLength for next run */ signatureLength1=signatureLength2=128; /* cleaning */ bctbx_DestroyEDDSAContext(aliceEDDSA); bctbx_DestroyECDHContext(aliceECDH); bctbx_DestroyEDDSAContext(bobEDDSA); bctbx_DestroyECDHContext(bobECDH); } bctbx_rng_context_free(RNG); } static void hash_test(void) { /* SHA patterns */ char const *sha_input = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; uint8_t sha256_pattern[] = {0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80, 0x03, 0x6c, 0xe5, 0x9e, 0x7b, 0x04, 0x92, 0x37, 0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0, 0x7a, 0x51, 0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1}; uint8_t sha384_pattern[] = {0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39}; uint8_t sha512_pattern[] = {0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09}; /* HMAC SHA patterns from RFC 4231 test case 7 */ std::vector hmac_sha_key{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; std::vector hmac_sha_data{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e}; std::vector hmac_sha256_pattern{0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2}; std::vector hmac_sha384_pattern{0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e}; std::vector hmac_sha512_pattern{0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5, 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb, 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58}; /* HMAC SHA 1 pattern from RFC 2202 */ std::vector hmac_sha1_key{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; std::string data = "Hi There"; std::vector hmac_sha1_data(data.begin(), data.end()); std::vector hmac_sha1_pattern{0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00}; uint8_t outputBuffer[64]; bctbx_sha256((uint8_t *)sha_input, strlen(sha_input), 32, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, sha256_pattern, 32)==0); bctbx_sha384((uint8_t *)sha_input, strlen(sha_input), 48, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, sha384_pattern, 48)==0); bctbx_sha512((uint8_t *)sha_input, strlen(sha_input), 64, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, sha512_pattern, 64)==0); /* C api */ bctbx_hmacSha1(hmac_sha1_key.data(), hmac_sha1_key.size(), hmac_sha1_data.data(), hmac_sha1_data.size(), 20, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha1_pattern.data(), 20)==0); bctbx_hmacSha256(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 32, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha256_pattern.data(), 32)==0); bctbx_hmacSha384(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 48, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha384_pattern.data(), 48)==0); bctbx_hmacSha512(hmac_sha_key.data(), hmac_sha_key.size(), hmac_sha_data.data(), hmac_sha_data.size(), 64, outputBuffer); BC_ASSERT_TRUE(memcmp(outputBuffer, hmac_sha512_pattern.data(), 64)==0); #ifdef HAVE_MBEDTLS /* C++ api */ BC_ASSERT_TRUE(std::equal(hmac_sha1_pattern.cbegin(), hmac_sha1_pattern.cend(), bctoolbox::HMAC(hmac_sha1_key, hmac_sha1_data).cbegin())); BC_ASSERT_TRUE(std::equal(hmac_sha256_pattern.cbegin(), hmac_sha256_pattern.cend(), bctoolbox::HMAC(hmac_sha_key, hmac_sha_data).cbegin())); BC_ASSERT_TRUE(std::equal(hmac_sha384_pattern.cbegin(), hmac_sha384_pattern.cend(), bctoolbox::HMAC(hmac_sha_key, hmac_sha_data).cbegin())); BC_ASSERT_TRUE(std::equal(hmac_sha384_pattern.cbegin(), hmac_sha384_pattern.cend(), bctoolbox::HMAC(hmac_sha_key, hmac_sha_data).cbegin())); /* HKDF test patterns from RFC5869 for SHA256 and generated for SHA512 using https://github.com/casebeer/python-hkdf */ /* test A.1 */ std::vector IKM{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; std::vector salt{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}; std::vector info{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; std::vector OKM{0x83, 0x23, 0x90, 0x08, 0x6c, 0xda, 0x71, 0xfb, 0x47, 0x62, 0x5b, 0xb5, 0xce, 0xb1, 0x68, 0xe4, 0xc8, 0xe2, 0x6a, 0x1a, 0x16, 0xed, 0x34, 0xd9, 0xfc, 0x7f, 0xe9, 0x2c, 0x14, 0x81, 0x57, 0x93, 0x38, 0xda, 0x36, 0x2c, 0xb8, 0xd9, 0xf9, 0x25, 0xd7, 0xcb}; BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); OKM.assign({0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); /* test A.2 */ IKM.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}); salt.assign({0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf}); info.assign({0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}); OKM.assign({0xce, 0x6c, 0x97, 0x19, 0x28, 0x05, 0xb3, 0x46, 0xe6, 0x16, 0x1e, 0x82, 0x1e, 0xd1, 0x65, 0x67, 0x3b, 0x84, 0xf4, 0x00, 0xa2, 0xb5, 0x14, 0xb2, 0xfe, 0x23, 0xd8, 0x4c, 0xd1, 0x89, 0xdd, 0xf1, 0xb6, 0x95, 0xb4, 0x8c, 0xbd, 0x1c, 0x83, 0x88, 0x44, 0x11, 0x37, 0xb3, 0xce, 0x28, 0xf1, 0x6a, 0xa6, 0x4b, 0xa3, 0x3b, 0xa4, 0x66, 0xb2, 0x4d, 0xf6, 0xcf, 0xcb, 0x02, 0x1e, 0xcf, 0xf2, 0x35, 0xf6, 0xa2, 0x05, 0x6c, 0xe3, 0xaf, 0x1d, 0xe4, 0x4d, 0x57, 0x20, 0x97, 0xa8, 0x50, 0x5d, 0x9e, 0x7a, 0x93}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); OKM.assign({0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); /* test A.3 */ IKM.assign({0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}); salt.clear(); info.clear(); OKM.assign({0xf5, 0xfa, 0x02, 0xb1, 0x82, 0x98, 0xa7, 0x2a, 0x8c, 0x23, 0x89, 0x8a, 0x87, 0x03, 0x47, 0x2c, 0x6e, 0xb1, 0x79, 0xdc, 0x20, 0x4c, 0x03, 0x42, 0x5c, 0x97, 0x0e, 0x3b, 0x16, 0x4b, 0xf9, 0x0f, 0xff, 0x22, 0xd0, 0x48, 0x36, 0xd0, 0xe2, 0x34, 0x3b, 0xac}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); OKM.assign({0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); /* test A.4 */ IKM.assign({0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}); salt.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}); info.assign({0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}); OKM.assign({0x74, 0x13, 0xe8, 0x99, 0x7e, 0x02, 0x06, 0x10, 0xfb, 0xf6, 0x82, 0x3f, 0x2c, 0xe1, 0x4b, 0xff, 0x01, 0x87, 0x5d, 0xb1, 0xca, 0x55, 0xf6, 0x8c, 0xfc, 0xf3, 0x95, 0x4d, 0xc8, 0xaf, 0xf5, 0x35, 0x59, 0xbd, 0x5e, 0x30, 0x28, 0xb0, 0x80, 0xf7, 0xc0, 0x68}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); /* test A.7 */ IKM.assign({0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c}); salt.clear(); info.clear(); OKM.assign({0x14, 0x07, 0xd4, 0x60, 0x13, 0xd9, 0x8b, 0xc6, 0xde, 0xce, 0xfc, 0xfe, 0xe5, 0x5f, 0x0f, 0x90, 0xb0, 0xc7, 0xf6, 0x3d, 0x68, 0xeb, 0x1a, 0x80, 0xea, 0xf0, 0x7e, 0x95, 0x3c, 0xfc, 0x0a, 0x3a, 0x52, 0x40, 0xa1, 0x55, 0xd6, 0xe4, 0xda, 0xa9, 0x65, 0xbb}); BC_ASSERT_TRUE(OKM == bctoolbox::HKDF(salt, IKM, info, OKM.size())); #endif // HAVE_MBEDTLS } #ifdef HAVE_MBEDTLS template static void rng_stats_update(size_t &count, double &mean, double &m2, U r) noexcept { count++; double delta = r - mean; mean += delta/count; double delta2 = r - mean; m2 += delta*delta2; } static void rng_test_32_args(size_t calls_nb) noexcept { size_t stat_count = 0; double stat_mean = 0; double stat_m2 = 0; try { for (size_t i=0; i1.1*0.5 || stat_mean<0.9*0.5) { BCTBX_SLOGE<<"RNG mean value on uint32_t running "<1.1/12 || stat_m2<0.9/12) { BCTBX_SLOGE<<"RNG variance value on uint32_t running "<(new bctoolbox::RNG()); for (size_t i=0; irandomize()); } // normalized mean shall be around 0.5 stat_mean /= std::pow(2,32)-1; stat_m2 /= stat_count*std::pow(std::pow(2,32)-1,2); if (stat_mean>1.1*0.5 || stat_mean<0.9*0.5) { BCTBX_SLOGE<<"RNG mean value on uint32_t running "<1.1/12 || stat_m2<0.9/12) { BCTBX_SLOGE<<"RNG variance value on uint32_t running "<1.1*0.5 || stat_mean<0.9*0.5) { BCTBX_SLOGE<<"RNG mean value on buffer size "<1.1/12 || stat_m2<0.9/12) { BCTBX_SLOGE<<"RNG variance value on buffer size "<(new bctoolbox::RNG()); for (size_t i=0; irandomize(alea, buffer_size); for (size_t j=0; j1.1*0.5 || stat_mean<0.9*0.5) { BCTBX_SLOGE<<"RNG mean value on buffer size "<1.1/12 || stat_m2<0.9/12) { BCTBX_SLOGE<<"RNG variance value on buffer size "< cipher{}; std::vector tag{}; std::vector plain{}; /* Test vectors for AES256-GCM128 from IEEE P1619.1/D22 - Annex D.3 */ /* Test D3.1*/ std::vector key{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; std::vector IV{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; std::vector pattern_plain{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; std::vector pattern_cipher{0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18}; std::vector pattern_tag{0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, 0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19}; std::vector AD{}; AD.clear(); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.2 */ key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; IV.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); AD.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); pattern_plain.clear(); pattern_cipher.clear(); pattern_tag={0x2d, 0x45, 0x55, 0x2d, 0x85, 0x75, 0x92, 0x2b, 0x3c, 0xa3, 0xcc, 0x53, 0x84, 0x42, 0xfa, 0x26}; cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(tag==pattern_tag); /* Test D3.3 */ key ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; IV.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); AD.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); pattern_plain.assign({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); pattern_cipher.assign({0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18}); pattern_tag={0xae, 0x9b, 0x17, 0x71, 0xdb, 0xa9, 0xcf, 0x62, 0xb3, 0x9b, 0xe0, 0x17, 0x94, 0x03, 0x30, 0xb4}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.4 */ key={0xfb, 0x76, 0x15, 0xb2, 0x3d, 0x80, 0x89, 0x1d, 0xd4, 0x70, 0x98, 0x0b, 0xc7, 0x95, 0x84, 0xc8, 0xb2, 0xfb, 0x64, 0xce, 0x60, 0x97, 0x8f, 0x4d, 0x17, 0xfc, 0xe4, 0x5a, 0x49, 0xe8, 0x30, 0xb7}; IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f}); AD.clear(); pattern_plain.assign({0xa8, 0x45, 0x34, 0x8e, 0xc8, 0xc5, 0xb5, 0xf1, 0x26, 0xf5, 0x0e, 0x76, 0xfe, 0xfd, 0x1b, 0x1e}); pattern_cipher.assign({0x5d, 0xf5, 0xd1, 0xfa, 0xbc, 0xbb, 0xdd, 0x05, 0x15, 0x38, 0x25, 0x24, 0x44, 0x17, 0x87, 0x04}); pattern_tag={0x4c, 0x43, 0xcc, 0xe5, 0xa5, 0x74, 0xd8, 0xa8, 0x8b, 0x43, 0xd4, 0x35, 0x3b, 0xd6, 0x0f, 0x9f}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.5 */ key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b}); AD.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13}); pattern_plain.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}); pattern_cipher.assign({0x59, 0x1b, 0x1f, 0xf2, 0x72, 0xb4, 0x32, 0x04, 0x86, 0x8f, 0xfc, 0x7b, 0xc7, 0xd5, 0x21, 0x99, 0x35, 0x26, 0xb6, 0xfa, 0x32, 0x24, 0x7c, 0x3c}); pattern_tag={0x7d, 0xe1, 0x2a, 0x56, 0x70, 0xe5, 0x70, 0xd8, 0xca, 0xe6, 0x24, 0xa1, 0x6d, 0xf0, 0x9c, 0x08}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.6 */ key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b}); AD.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}); pattern_plain.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}); pattern_cipher.assign({0x59, 0x1b, 0x1f, 0xf2, 0x72, 0xb4, 0x32, 0x04, 0x86, 0x8f, 0xfc, 0x7b, 0xc7, 0xd5, 0x21, 0x99, 0x35, 0x26, 0xb6, 0xfa, 0x32, 0x24, 0x7c, 0x3c, 0x40, 0x57, 0xf3, 0xea, 0xe7, 0x54, 0x8c, 0xef}); pattern_tag={0xa1, 0xde, 0x55, 0x36, 0xe9, 0x7e, 0xdd, 0xdc, 0xcd, 0x26, 0xee, 0xb1, 0xb5, 0xff, 0x7b, 0x32}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); std::vector repeatAD{}; for (auto i=0; i<256; i++) { repeatAD.insert(repeatAD.end(), AD.cbegin(), AD.cend()); } cipher = AEADEncrypt(key, IV, pattern_plain, repeatAD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, repeatAD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.7 */ key={0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; IV.assign({0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b}); AD.assign({0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}); pattern_plain.assign({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}); pattern_cipher.assign({0x79, 0x3b, 0x3f, 0xd2, 0x52, 0x94, 0x12, 0x24, 0xa6, 0xaf, 0xdc, 0x5b, 0xe7, 0xf5, 0x01, 0xb9, 0x15, 0x06, 0x96, 0xda, 0x12, 0x04, 0x5c, 0x1c, 0x60, 0x77, 0xd3, 0xca, 0xc7, 0x74, 0xac, 0xcf, 0xc3, 0xd5, 0x30, 0xd8, 0x48, 0xd6, 0x65, 0xd8, 0x1a, 0x49, 0xcb, 0xb5, 0x00, 0xb8, 0x8b, 0xbb, 0x62, 0x4a, 0xe6, 0x1d, 0x16, 0x67, 0x22, 0x9c, 0x30, 0x2d, 0xc6, 0xff, 0x0b, 0xb4, 0xd7, 0x0b, 0xdb, 0xbc, 0x85, 0x66, 0xd6, 0xf5, 0xb1, 0x58, 0xda, 0x99, 0xa2, 0xff, 0x2e, 0x01, 0xdd, 0xa6, 0x29, 0xb8, 0x9c, 0x34, 0xad, 0x1e, 0x5f, 0xeb, 0xa7, 0x0e, 0x7a, 0xae, 0x43, 0x28, 0x28, 0x9c, 0x36, 0x29, 0xb0, 0x58, 0x83, 0x50, 0x58, 0x1c, 0xa8, 0xb9, 0x7c, 0xcf, 0x12, 0x58, 0xfa, 0x3b, 0xbe, 0x2c, 0x50, 0x26, 0x04, 0x7b, 0xa7, 0x26, 0x48, 0x96, 0x9c, 0xff, 0x8b, 0xa1, 0x0a, 0xe3, 0x0e, 0x05, 0x93, 0x5d, 0xf0, 0xc6, 0x93, 0x74, 0x18, 0x92, 0xb7, 0x6f, 0xaf, 0x67, 0x13, 0x3a, 0xbd, 0x2c, 0xf2, 0x03, 0x11, 0x21, 0xbd, 0x8b, 0xb3, 0x81, 0x27, 0xa4, 0xd2, 0xee, 0xde, 0xea, 0x13, 0x27, 0x64, 0x94, 0xf4, 0x02, 0xcd, 0x7c, 0x10, 0x7f, 0xb3, 0xec, 0x3b, 0x24, 0x78, 0x48, 0x34, 0x33, 0x8e, 0x55, 0x43, 0x62, 0x87, 0x09, 0x2a, 0xc4, 0xa2, 0x6f, 0x5e, 0xa7, 0xea, 0x4a, 0xd6, 0x8d, 0x73, 0x15, 0x16, 0x39, 0xb0, 0x5b, 0x24, 0xe6, 0x8b, 0x98, 0x16, 0xd1, 0x39, 0x83, 0x76, 0xd8, 0xe4, 0x13, 0x85, 0x94, 0x75, 0x8d, 0xb9, 0xad, 0x3b, 0x40, 0x92, 0x59, 0xb2, 0x6d, 0xcf, 0xc0, 0x6e, 0x72, 0x2b, 0xe9, 0x87, 0xb3, 0x76, 0x7f, 0x70, 0xa7, 0xb8, 0x56, 0xb7, 0x74, 0xb1, 0xba, 0x26, 0x85, 0xb3, 0x68, 0x09, 0x14, 0x29, 0xfc, 0xcb, 0x8d, 0xcd, 0xde, 0x09, 0xe4}); pattern_tag={0x87, 0xec, 0x83, 0x7a, 0xbf, 0x53, 0x28, 0x55, 0xb2, 0xce, 0xa1, 0x69, 0xd6, 0x94, 0x3f, 0xcd}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.8 */ key={0xfb, 0x76, 0x15, 0xb2, 0x3d, 0x80, 0x89, 0x1d, 0xd4, 0x70, 0x98, 0x0b, 0xc7, 0x95, 0x84, 0xc8, 0xb2, 0xfb, 0x64, 0xce, 0x60, 0x97, 0x87, 0x8d, 0x17, 0xfc, 0xe4, 0x5a, 0x49, 0xe8, 0x30, 0xb7}; IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f}); AD.assign({0x36}); pattern_plain.assign({0xa9}); pattern_cipher.assign({0x0a}); pattern_tag={0xbe, 0x98, 0x7d, 0x00, 0x9a, 0x4b, 0x34, 0x9a, 0xa8, 0x0c, 0xb9, 0xc4, 0xeb, 0xc1, 0xe9, 0xf4}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.9 */ key={0xf8, 0xd4, 0x76, 0xcf, 0xd6, 0x46, 0xea, 0x6c, 0x23, 0x84, 0xcb, 0x1c, 0x27, 0xd6, 0x19, 0x5d, 0xfe, 0xf1, 0xa9, 0xf3, 0x7b, 0x9c, 0x8d, 0x21, 0xa7, 0x9c, 0x21, 0xf8, 0xcb, 0x90, 0xd2, 0x89}; IV.assign({0xdb, 0xd1, 0xa3, 0x63, 0x60, 0x24, 0xb7, 0xb4, 0x02, 0xda, 0x7d, 0x6f}); AD.assign({0x7b, 0xd8, 0x59, 0xa2, 0x47, 0x96, 0x1a, 0x21, 0x82, 0x3b, 0x38, 0x0e, 0x9f, 0xe8, 0xb6, 0x50, 0x82, 0xba, 0x61, 0xd3}); pattern_plain.assign({0x90, 0xae, 0x61, 0xcf, 0x7b, 0xae, 0xbd, 0x4c, 0xad, 0xe4, 0x94, 0xc5, 0x4a, 0x29, 0xae, 0x70, 0x26, 0x9a, 0xec, 0x71}); pattern_cipher.assign({0xce, 0x20, 0x27, 0xb4, 0x7a, 0x84, 0x32, 0x52, 0x01, 0x34, 0x65, 0x83, 0x4d, 0x75, 0xfd, 0x0f, 0x07, 0x29, 0x75, 0x2e}); pattern_tag={0xac, 0xd8, 0x83, 0x38, 0x37, 0xab, 0x0e, 0xde, 0x84, 0xf4, 0x74, 0x8d, 0xa8, 0x89, 0x9c, 0x15}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.10 */ key={0xdb, 0xbc, 0x85, 0x66, 0xd6, 0xf5, 0xb1, 0x58, 0xda, 0x99, 0xa2, 0xff, 0x2e, 0x01, 0xdd, 0xa6, 0x29, 0xb8, 0x9c, 0x34, 0xad, 0x1e, 0x5f, 0xeb, 0xa7, 0x0e, 0x7a, 0xae, 0x43, 0x28, 0x28, 0x9c}; IV.assign({0xcf, 0xc0, 0x6e, 0x72, 0x2b, 0xe9, 0x87, 0xb3, 0x76, 0x7f, 0x70, 0xa7, 0xb8, 0x56, 0xb7, 0x74}); AD.clear(); pattern_plain.assign({0xce, 0x20, 0x27, 0xb4, 0x7a, 0x84, 0x32, 0x52, 0x01, 0x34, 0x65, 0x83, 0x4d, 0x75, 0xfd, 0x0f}); pattern_cipher.assign({0xdc, 0x03, 0xe5, 0x24, 0x83, 0x0d, 0x30, 0xf8, 0x8e, 0x19, 0x7f, 0x3a, 0xca, 0xce, 0x66, 0xef}); pattern_tag={0x99, 0x84, 0xef, 0xf6, 0x90, 0x57, 0x55, 0xd1, 0x83, 0x6f, 0x2d, 0xb0, 0x40, 0x89, 0x63, 0x4c}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.11 */ key={0x0e, 0x05, 0x93, 0x5d, 0xf0, 0xc6, 0x93, 0x74, 0x18, 0x92, 0xb7, 0x6f, 0xaf, 0x67, 0x13, 0x3a, 0xbd, 0x2c, 0xf2, 0x03, 0x11, 0x21, 0xbd, 0x8b, 0xb3, 0x81, 0x27, 0xa4, 0xd2, 0xee, 0xde, 0xea}; IV.assign({0x74, 0xb1, 0xba, 0x26, 0x85, 0xb3, 0x68, 0x09, 0x14, 0x29, 0xfc, 0xcb, 0x8d, 0xcd, 0xde, 0x09, 0xe4}); AD.assign({0x7b, 0xd8, 0x59, 0xa2, 0x47, 0x96, 0x1a, 0x21, 0x82, 0x3b, 0x38, 0x0e, 0x9f, 0xe8, 0xb6, 0x50, 0x82, 0xba, 0x61, 0xd3}); pattern_plain.assign({0x90, 0xae, 0x61, 0xcf, 0x7b, 0xae, 0xbd, 0x4c, 0xad, 0xe4, 0x94, 0xc5, 0x4a, 0x29, 0xae, 0x70, 0x26, 0x9a, 0xec, 0x71}); pattern_cipher.assign({0x6b, 0xe6, 0x5e, 0x56, 0x06, 0x6c, 0x40, 0x56, 0x73, 0x8c, 0x03, 0xfe, 0x23, 0x20, 0x97, 0x4b, 0xa3, 0xf6, 0x5e, 0x09}); pattern_tag={0x61, 0x08, 0xdc, 0x41, 0x7b, 0xf3, 0x2f, 0x7f, 0xb7, 0x55, 0x4a, 0xe5, 0x2f, 0x08, 0x8f, 0x87}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); /* Test D3.12 */ key={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; IV.assign({0x02, 0xcb, 0xbc, 0x7a, 0x03, 0xeb, 0x4d, 0xe3, 0x9d, 0x80, 0xd1, 0xeb, 0xc9, 0x88, 0xbf, 0xdf}); AD.assign({0x68, 0x8e, 0x1a, 0xa9, 0x84, 0xde, 0x92, 0x6d, 0xc7, 0xb4, 0xc4, 0x7f, 0x44}); pattern_plain.assign({0xa2, 0xaa, 0xb3, 0xad, 0x8b, 0x17, 0xac, 0xdd, 0xa2, 0x88, 0x42, 0x6c, 0xd7, 0xc4, 0x29, 0xb7, 0xca, 0x86, 0xb7, 0xac, 0xa0, 0x58, 0x09, 0xc7, 0x0c, 0xe8, 0x2d, 0xb2, 0x57, 0x11, 0xcb, 0x53, 0x02, 0xeb, 0x27, 0x43, 0xb0, 0x36, 0xf3, 0xd7, 0x50, 0xd6, 0xcf, 0x0d, 0xc0, 0xac, 0xb9, 0x29, 0x50, 0xd5, 0x46, 0xdb, 0x30, 0x8f, 0x93, 0xb4, 0xff, 0x24, 0x4a, 0xfa, 0x9d, 0xc7, 0x2b, 0xcd, 0x75, 0x8d, 0x2c}); pattern_cipher.assign({0xee, 0x62, 0x55, 0x2a, 0xeb, 0xc0, 0xc3, 0xc7, 0xda, 0xae, 0x12, 0xbb, 0x6c, 0x32, 0xca, 0x5a, 0x00, 0x5f, 0x4a, 0x1a, 0xaa, 0xb0, 0x04, 0xed, 0x0f, 0x0b, 0x30, 0xab, 0xbf, 0x15, 0xac, 0xf4, 0xc5, 0x0c, 0x59, 0x66, 0x2d, 0x4b, 0x44, 0x68, 0x41, 0x95, 0x44, 0xe7, 0xf9, 0x81, 0x97, 0x35, 0x63, 0xce, 0x55, 0x6a, 0xe5, 0x08, 0x59, 0xee, 0x09, 0xb1, 0x4d, 0x31, 0xa0, 0x53, 0x98, 0x6f, 0x9a, 0xc8, 0x9b}); pattern_tag={0x9c, 0xd0, 0xdb, 0x93, 0x6e, 0x26, 0xd4, 0x4b, 0xe9, 0x74, 0xba, 0x86, 0x82, 0x85, 0xa2, 0xe1}; cipher.resize(pattern_plain.size()); plain.resize(pattern_plain.size()); cipher = AEADEncrypt(key, IV, pattern_plain, AD, tag); BC_ASSERT_TRUE(cipher==pattern_cipher); BC_ASSERT_TRUE(tag==pattern_tag); BC_ASSERT_TRUE(AEADDecrypt(key, IV, pattern_cipher, AD, pattern_tag, plain)); BC_ASSERT_TRUE(plain==pattern_plain); } static void key_wrap_test(){ int ret; std::vector plaintext; std::vector key; std::vector ciphertext; std::vector wrapped_ct; std::vector unwrapped_pt; /* Test vectors from Section 4.1 of the RFC 3394 : https://datatracker.ietf.org/doc/html/rfc3394#section-4.1 */ /*plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; ciphertext = {0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5}; ret = AES_key_wrap(plaintext, key, wrapped_ct); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(ciphertext==wrapped_ct); ret = AES_key_unwrap(ciphertext, key, unwrapped_pt); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(plaintext==unwrapped_pt); plaintext.clear(); key.clear(); ciphertext.clear(); wrapped_ct.clear();*/ /* Test vectors from Section 6 of the RFC 5649 : https://datatracker.ietf.org/doc/html/rfc5649#section-6 */ plaintext = {0xc3, 0x7b, 0x7e, 0x64, 0x92, 0x58, 0x43, 0x40, 0xbe, 0xd1, 0x22, 0x07, 0x80, 0x89, 0x41, 0x15, 0x50, 0x68, 0xf7, 0x38}; key = {0x58, 0x40, 0xdf, 0x6e, 0x29, 0xb0, 0x2a, 0xf1, 0xab, 0x49, 0x3b, 0x70, 0x5b, 0xf1, 0x6e, 0xa1, 0xae, 0x83, 0x38, 0xf4, 0xdc, 0xc1, 0x76, 0xa8}; ciphertext = {0x13, 0x8b, 0xde, 0xaa, 0x9b, 0x8f, 0xa7, 0xfc, 0x61, 0xf9, 0x77, 0x42, 0xe7, 0x22, 0x48, 0xee, 0x5a, 0xe6, 0xae, 0x53, 0x60, 0xd1, 0xae, 0x6a, 0x5f, 0x54, 0xf3, 0x73, 0xfa, 0x54, 0x3b, 0x6a}; ret = AES_key_wrap(plaintext, key, wrapped_ct, AesId::AES192); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(ciphertext==wrapped_ct); ret = AES_key_unwrap(ciphertext, key, unwrapped_pt, AesId::AES192); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(plaintext==unwrapped_pt); plaintext.clear(); unwrapped_pt.clear(); key.clear(); ciphertext.clear(); wrapped_ct.clear(); plaintext = {0x46, 0x6f, 0x72, 0x50, 0x61, 0x73, 0x69}; key = {0x58, 0x40, 0xdf, 0x6e, 0x29, 0xb0, 0x2a, 0xf1, 0xab, 0x49, 0x3b, 0x70, 0x5b, 0xf1, 0x6e, 0xa1, 0xae, 0x83, 0x38, 0xf4, 0xdc, 0xc1, 0x76, 0xa8}; ciphertext = {0xaf, 0xbe, 0xb0, 0xf0, 0x7d, 0xfb, 0xf5, 0x41, 0x92, 0x00, 0xf2, 0xcc, 0xb5, 0x0b, 0xb2, 0x4f}; ret = AES_key_wrap(plaintext, key, wrapped_ct, AesId::AES192); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(ciphertext==wrapped_ct); ret = AES_key_unwrap(ciphertext, key, unwrapped_pt, AesId::AES192); BC_ASSERT_EQUAL(ret, 0, int, "%0x"); BC_ASSERT_TRUE(plaintext==unwrapped_pt); } static test_t crypto_tests[] = { TEST_NO_TAG("Diffie-Hellman Key exchange", DHM), TEST_NO_TAG("Elliptic Curve Diffie-Hellman Key exchange", ECDH), TEST_NO_TAG("ECDH25519 decaf-mbedtls", ECDH25519compat), TEST_NO_TAG("EdDSA sign and verify", EdDSA), TEST_NO_TAG("Ed25519 to X25519 key conversion", ed25519_to_x25519_keyconversion), TEST_NO_TAG("Sign message and exchange key using the same base secret", sign_and_key_exchange), TEST_NO_TAG("Hash functions", hash_test), TEST_NO_TAG("RNG", rng_test), TEST_NO_TAG("AEAD", AEAD), TEST_NO_TAG("Key wrap", key_wrap_test), }; test_suite_t crypto_test_suite = {"Crypto", NULL, NULL, NULL, NULL, sizeof(crypto_tests) / sizeof(crypto_tests[0]), crypto_tests}; bctoolbox-5.2.0/tester/encrypted_vfs.cc000066400000000000000000000450631434566643100202250ustar00rootroot00000000000000/* * Copyright (c) 2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/vfs_encrypted.hh" #include "bctoolbox/vfs_standard.h" #include "bctoolbox/logging.h" #include using namespace bctoolbox; /* A callback to position the key material and algorithm suite to use */ static EncryptedVfsOpenCb set_dummy_encryption_info([](VfsEncryption &settings) { const std::vector keyMaterial{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; settings.encryptionSuiteSet(EncryptionSuite::dummy); settings.secretMaterialSet(keyMaterial); settings.chunkSizeSet(16); }); static EncryptedVfsOpenCb set_plain_encryption_info([](VfsEncryption &settings) { settings.encryptionSuiteSet(EncryptionSuite::plain); }); static EncryptedVfsOpenCb set_aes256_encryption_info([](VfsEncryption &settings) { const std::vector keyMaterial{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xf0, 0x11, 0x12, 0x13, 0x54, 0x55, 0x56, 0xa7, 0xa8, 0xa9, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xef}; settings.encryptionSuiteSet(EncryptionSuite::aes256gcm128_sha256); settings.secretMaterialSet(keyMaterial); settings.chunkSizeSet(16); }); static EncryptedVfsOpenCb set_encryption_info([](VfsEncryption &settings) { auto filename = settings.filenameGet(); if (filename.find(bctoolbox::encryptionSuiteString(bctoolbox::EncryptionSuite::plain)) != std::string::npos) { set_plain_encryption_info(settings); } else if (filename.find(bctoolbox::encryptionSuiteString(bctoolbox::EncryptionSuite::aes256gcm128_sha256)) != std::string::npos) { set_aes256_encryption_info(settings); } else if (filename.find(bctoolbox::encryptionSuiteString(bctoolbox::EncryptionSuite::dummy)) != std::string::npos) { set_dummy_encryption_info(settings); } else { throw BCTBX_EXCEPTION<<"Try to set unknown encryption suite"; } }); /* a message to write in files */ static const uint8_t message[256] = {0x42, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; /** * Block size for tests is 16 bytes * if closeFile is true, close and reopen file afet each write operation */ void basic_encryption_test(bctoolbox::EncryptionSuite suite, bool closeFile = true) { // used to check zero readings uint8_t zero_buff[256]; memset (zero_buff, 0, sizeof(zero_buff)); /* get the encrypted file path */ char *path = bc_tester_file("basic."); std::string filePath{path}; filePath.append(bctoolbox::encryptionSuiteString(suite)).append(".evfs"); bctbx_free(path); /* remove file if it was already there */ remove(filePath.data()); /* create the file */ bctbx_vfs_file_t *fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); uint8_t readBuffer[256]; memset(readBuffer, 0, sizeof(readBuffer)); /* Make simple write, size is < block size and check we get back what we just wrote */ bctbx_file_write(fp, message, 4, 0); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 4, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 4, 0), 4, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 4)==0); memset(readBuffer, 0, sizeof(readBuffer)); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 32, 0), 4, ssize_t, "%ld"); // try to read more than we have BC_ASSERT_TRUE(memcmp(readBuffer, message, 4)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* Write a whole block completely overwritting previous data */ bctbx_file_write(fp, message, 16, 0); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 16, int64_t, "%ld"); bctbx_file_read(fp, readBuffer, 16, 0); BC_ASSERT_TRUE(memcmp(readBuffer, message, 4)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* Partial overwrite of the end of first block */ bctbx_file_write(fp, message, 8, 8); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 16, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 0), 16, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 8)==0); BC_ASSERT_TRUE(memcmp(readBuffer+8, message, 8)==0); memset(readBuffer, 0, sizeof(readBuffer)); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 8), 8, ssize_t, "%ld"); // read at index not 8, we have only 8 bytes to get ask for 16 anyway BC_ASSERT_TRUE(memcmp(readBuffer, message, 8)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* Partial overwrite of the begining of first block */ bctbx_file_write(fp, message+16, 8, 0); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 16, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 0), 16, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message+16, 8)==0); BC_ASSERT_TRUE(memcmp(readBuffer+8, message, 8)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* truncate the file to a size smaller than it was */ BC_ASSERT_EQUAL(bctbx_file_truncate(fp,8), 0, size_t, "%ld"); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 8, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 8), 0, ssize_t, "%ld"); // read after the end of the file -> nothing shall get back BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 0), 8, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message+16, 8)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* truncate the file to a size 0 */ BC_ASSERT_EQUAL(bctbx_file_truncate(fp,0), 0, size_t, "%ld"); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 0, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 0), 0, ssize_t, "%ld"); /* write on several blocks, starting after the current end of file */ bctbx_file_write(fp, message, 65, 15); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 80, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 80, 0), 80, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, zero_buff, 15)==0); BC_ASSERT_TRUE(memcmp(readBuffer+15, message, 65)==0); memset(readBuffer, 0, sizeof(readBuffer)); /* overwrite on several blocks */ bctbx_file_write(fp, message, 18, 31); if (closeFile) { bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); } BC_ASSERT_EQUAL(bctbx_file_size(fp), 80, int64_t, "%ld"); // read the part left there from offset 15 to 31 BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 15), 16, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 16)==0); memset(readBuffer, 0, sizeof(readBuffer)); // read the part we just wrote BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 18, 31), 18, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 18)==0); memset(readBuffer, 0, sizeof(readBuffer)); // read the part left after BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 80, 49), 31, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message+34, 31)==0); memset(readBuffer, 0, sizeof(readBuffer)); bctbx_file_close(fp); // When using the plain, check with the standard the content of file if (suite == bctoolbox::EncryptionSuite::plain ) { fp = bctbx_file_open2(&bcStandardVfs, filePath.data(), O_RDWR); BC_ASSERT_EQUAL(bctbx_file_size(fp), 80, int64_t, "%ld"); // read the part left there from offset 15 to 31 BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 16, 15), 16, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 16)==0); memset(readBuffer, 0, sizeof(readBuffer)); // read the part we just wrote BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 18, 31), 18, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 18)==0); memset(readBuffer, 0, sizeof(readBuffer)); // read the part left after BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 80, 49), 31, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message+34, 31)==0); memset(readBuffer, 0, sizeof(readBuffer)); bctbx_file_close(fp); } /* cleaning */ remove(filePath.data()); } void basic_encryption_test() { /* set the encrypted vfs callback */ VfsEncryption::openCallbackSet(set_encryption_info); basic_encryption_test(EncryptionSuite::dummy, false); basic_encryption_test(EncryptionSuite::dummy, true); basic_encryption_test(EncryptionSuite::plain, false); basic_encryption_test(EncryptionSuite::plain, true); basic_encryption_test(EncryptionSuite::aes256gcm128_sha256, false); basic_encryption_test(EncryptionSuite::aes256gcm128_sha256, true); VfsEncryption::openCallbackSet(nullptr); } /** * create an encrypted file, * open it with regular API, * modify header data, * try to re-open it */ void auth_fail_test(bctoolbox::EncryptionSuite suite) { /* get the encrypted file path */ char *path = bc_tester_file("auth_fail."); std::string filePath{path}; filePath.append(bctoolbox::encryptionSuiteString(suite)).append(".evfs"); bctbx_free(path); /* remove file if it was already there */ remove(filePath.data()); /* create the file */ bctbx_vfs_file_t *fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); uint8_t readBuffer[1024]; memset(readBuffer, 0, sizeof(readBuffer)); /* Write something */ bctbx_file_write(fp, message, sizeof(message), 8); /* close and re-open */ bctbx_file_close(fp); fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); /* check we can read what we wrote*/ BC_ASSERT_EQUAL(bctbx_file_size(fp), sizeof(message)+8, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, sizeof(message), 8), sizeof(message), ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, sizeof(message))==0); memset(readBuffer, 0, sizeof(readBuffer)); /* close */ bctbx_file_close(fp); /* now open directly the file, and modify one byte in the header */ std::fstream file(filePath, std::ios::out | std::ios::in | std::ios::binary); file.seekg(30); // base header file is 29 bytes, at 30 we are in the integrity data of the encryption suite, change one byte char tweakBuf[2]; file.read(tweakBuf, 1); file.seekp(30); tweakBuf[0] ^= 0xFF; // modify the byte read file.write(tweakBuf, 1); file.close(); /* reopen the file with the bctoolbox fvs API, it shall fail */ fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); BC_ASSERT_TRUE(fp==NULL); if (fp!=NULL) { bctbx_file_close(fp); } /* cleaning */ remove(filePath.data()); } void auth_fail_test() { /* set the encrypted vfs callback */ VfsEncryption::openCallbackSet(set_encryption_info); auth_fail_test(EncryptionSuite::dummy); auth_fail_test(EncryptionSuite::aes256gcm128_sha256); VfsEncryption::openCallbackSet(nullptr); } /** * Write a plain file using standard vfs * Open it using encrypted one * Check the migration was done */ void migration_test(bctoolbox::EncryptionSuite suite) { // get the file path char *path = bc_tester_file("migration."); std::string filePath{path}; filePath.append(bctoolbox::encryptionSuiteString(suite)).append(".evfs"); bctbx_free(path); // remove file if it was already there remove(filePath.data()); // create the file using standard vfs bctbx_vfs_file_t *fp = bctbx_file_open2(bctbx_vfs_get_standard(), filePath.data(), O_RDWR|O_CREAT); uint8_t readBuffer[256]; memset(readBuffer, 0, sizeof(readBuffer)); // Make simple write bctbx_file_write(fp, message, 42, 0); BC_ASSERT_EQUAL(bctbx_file_size(fp), 42, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 42, 0), 42, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 42)==0); memset(readBuffer, 0, sizeof(readBuffer)); // file shall not be encrypted BC_ASSERT_FALSE(bctbx_file_is_encrypted(fp)); // close file bctbx_file_close(fp); // open it read only using the encrypted vfs, it shall NOT force the migration fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDONLY); // readings test again BC_ASSERT_EQUAL(bctbx_file_size(fp), 42, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 42, 0), 42, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 42)==0); // now it shall still be plain BC_ASSERT_FALSE(bctbx_file_is_encrypted(fp)); bctbx_file_close(fp); // open it using the encrypted vfs, it shall force the migration fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR); // readings test again BC_ASSERT_EQUAL(bctbx_file_size(fp), 42, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 42, 0), 42, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 42)==0); // now it shall be encrypted BC_ASSERT_TRUE(bctbx_file_is_encrypted(fp)); bctbx_file_close(fp); // cleaning std::remove(filePath.data()); } void migration_test() { /* set the encrypted vfs callback */ VfsEncryption::openCallbackSet(set_encryption_info); migration_test(EncryptionSuite::dummy); migration_test(EncryptionSuite::aes256gcm128_sha256); VfsEncryption::openCallbackSet(nullptr); } void recovery_test(bctoolbox::EncryptionSuite suite) { /* get the encrypted file path */ char *path = bc_tester_file("recovery."); std::string filePath{path}; filePath.append(bctoolbox::encryptionSuiteString(suite)).append(".evfs"); bctbx_free(path); /* remove file if it was already there */ remove(filePath.data()); /* create the file */ bctbx_vfs_file_t *fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); uint8_t readBuffer[256]; memset(readBuffer, 0, sizeof(readBuffer)); // Make simple write, block size is 16 bctbx_file_write(fp, message, 256, 0); // Check it worked BC_ASSERT_EQUAL(bctbx_file_size(fp), 256, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 256, 0), 256, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 256)==0); memset(readBuffer, 0, sizeof(readBuffer)); // Close the file bctbx_file_close(fp); // reopen it directly and read the header // base header file is 29, dummy module adds 16 bytes, aes256gcm128 adds 48 bytes std::fstream file (filePath, std::ios::in | std::ios::binary); char fileHeader[48+29]; auto fileHeaderSize = (suite==bctoolbox::EncryptionSuite::dummy)?(29+16):(29+48); file.seekg(0, std::ios::beg); file.read(fileHeader, fileHeaderSize); file.close(); // Open it again with the eVFS and truncate it fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); BC_ASSERT_EQUAL(bctbx_file_truncate(fp,142), 0, int, "%d"); BC_ASSERT_EQUAL(bctbx_file_size(fp), 142, int64_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 256, 0), 142, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 142)==0); memset(readBuffer, 0, sizeof(readBuffer)); bctbx_file_close(fp); // Open it directly and rewrite the header as it was before to simulate an error occuring before the call to writeHeader but after the file modification // So header is still a valid one but the size won't match std::fstream ofile (filePath, std::ios::in | std::ios::out | std::ios::binary); ofile.seekp(0, std::ios::beg); ofile.write(fileHeader, fileHeaderSize); ofile.close(); // Open it again, the file should self heal fp = bctbx_file_open2(&bcEncryptedVfs, filePath.data(), O_RDWR|O_CREAT); BC_ASSERT_PTR_NOT_NULL(fp); // check the content BC_ASSERT_EQUAL(bctbx_file_size(fp), 142, size_t, "%ld"); BC_ASSERT_EQUAL(bctbx_file_read(fp, readBuffer, 256, 0), 142, ssize_t, "%ld"); BC_ASSERT_TRUE(memcmp(readBuffer, message, 142)==0); memset(readBuffer, 0, sizeof(readBuffer)); bctbx_file_close(fp); // cleaning //std::remove(filePath.data()); } void recovery_test() { /* set the encrypted vfs callback */ VfsEncryption::openCallbackSet(set_encryption_info); recovery_test(EncryptionSuite::dummy); recovery_test(EncryptionSuite::aes256gcm128_sha256); VfsEncryption::openCallbackSet(nullptr); } static test_t encrypted_vfs_tests[] = { TEST_NO_TAG("basic", basic_encryption_test), TEST_NO_TAG("Authentication failure", auth_fail_test), TEST_NO_TAG("migration", migration_test), TEST_NO_TAG("recovery", recovery_test) }; test_suite_t encrypted_vfs_test_suite = {"Encrypted vfs", NULL, NULL, NULL, NULL, sizeof(encrypted_vfs_tests) / sizeof(encrypted_vfs_tests[0]), encrypted_vfs_tests}; bctoolbox-5.2.0/tester/ios_utils.cc000066400000000000000000000025651434566643100173640ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/ios_utils.hh" using namespace bctoolbox; static void ios_utils_return_values(void) { auto &iOSUtils = IOSUtils::getUtils(); BC_ASSERT_EQUAL(iOSUtils.beginBackgroundTask(nullptr, nullptr), 0, unsigned long, "%lu"); BC_ASSERT_EQUAL(iOSUtils.isApplicationStateActive(), false, bool, "%d"); } static test_t ios_utils_tests[] = { TEST_NO_TAG("Return values for stubbed functions", ios_utils_return_values), }; test_suite_t ios_utils_test_suite = {"iOS Utilities", NULL, NULL, NULL, NULL, sizeof(ios_utils_tests) / sizeof(ios_utils_tests[0]), ios_utils_tests}; bctoolbox-5.2.0/tester/param_string.c000066400000000000000000000044411434566643100176700ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/param_string.h" static void get_value_test(void) { size_t result_len = 10; char * result = bctbx_malloc(result_len); char * paramString = ""; BC_ASSERT_FALSE(bctbx_param_string_get_value(paramString, "param", result, result_len)); paramString = "param=true"; BC_ASSERT_TRUE(bctbx_param_string_get_value(paramString, "param", result, result_len)); BC_ASSERT_TRUE(strcmp(result, "true") == 0); BC_ASSERT_FALSE(bctbx_param_string_get_value(paramString, "notparam", result, result_len)); paramString = "test;param=true;test"; BC_ASSERT_TRUE(bctbx_param_string_get_value(paramString, "param", result, result_len)); BC_ASSERT_TRUE(strcmp(result, "true") == 0); BC_ASSERT_FALSE(bctbx_param_string_get_value(paramString, "notparam", result, result_len)); } static void get_bool_value_test(void) { char * paramString = ""; BC_ASSERT_FALSE(bctbx_param_string_get_bool_value(paramString, "param")); paramString = "param=false"; BC_ASSERT_FALSE(bctbx_param_string_get_bool_value(paramString, "param")); paramString = "param=42"; BC_ASSERT_FALSE(bctbx_param_string_get_bool_value(paramString, "param")); paramString = "param=true"; BC_ASSERT_TRUE(bctbx_param_string_get_bool_value(paramString, "param")); } static test_t param_string_tests[] = { TEST_NO_TAG("Get value", get_value_test), TEST_NO_TAG("Get bool value", get_bool_value_test) }; test_suite_t param_string_test_suite = {"Param string", NULL, NULL, NULL, NULL, sizeof(param_string_tests) / sizeof(param_string_tests[0]), param_string_tests}; bctoolbox-5.2.0/tester/parser.c000066400000000000000000000031531434566643100164750ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/parser.h" static void simple_escaping(void) { char * my_escaped_string; bctbx_noescape_rules_t my_rules = {0}; bctbx_noescape_rules_add_alfanums(my_rules); my_escaped_string = bctbx_escape("François",my_rules); BC_ASSERT_TRUE(strcmp("Fran%c3%a7ois",my_escaped_string)==0); bctbx_free(my_escaped_string); } static void simple_unescaping(void) { char * my_unescaped_string; my_unescaped_string = bctbx_unescaped_string("Fran%c3%a7ois"); BC_ASSERT_TRUE(strcmp("François",my_unescaped_string)==0); bctbx_free(my_unescaped_string); } static test_t container_tests[] = { TEST_NO_TAG("simple escaping", simple_escaping), TEST_NO_TAG("simple unescaping", simple_unescaping), }; test_suite_t parser_test_suite = {"Parsing", NULL, NULL, NULL, NULL, sizeof(container_tests) / sizeof(container_tests[0]), container_tests}; bctoolbox-5.2.0/tester/port.c000066400000000000000000000256711434566643100161760ustar00rootroot00000000000000/* * Copyright (c) 2016-2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/port.h" #include "bctoolbox/vfs.h" static void bytes_to_from_hexa_strings(void) { const uint8_t a55aBytes[2] = {0xa5, 0x5a}; const uint8_t a55aString[5] = "a55a"; const uint8_t upBytes[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; const uint8_t upString[17] = "0123456789abcdef"; const uint8_t downBytes[8] = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}; const uint8_t downString[17] = "fedcba9876543210"; uint8_t outputBytes[16]; uint8_t outputString[16]; BC_ASSERT_EQUAL(bctbx_char_to_byte("1"[0]), 1, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_char_to_byte("5"[0]), 5, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_char_to_byte("a"[0]), 10, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_char_to_byte("e"[0]), 14, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_char_to_byte("B"[0]), 11, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_char_to_byte("F"[0]), 15, uint8_t, "%d"); BC_ASSERT_EQUAL(bctbx_byte_to_char(0), "0"[0], char, "%c"); BC_ASSERT_EQUAL(bctbx_byte_to_char(2), "2"[0], char, "%c"); BC_ASSERT_EQUAL(bctbx_byte_to_char(5), "5"[0], char, "%c"); BC_ASSERT_EQUAL(bctbx_byte_to_char(0x0a), "a"[0], char, "%c"); BC_ASSERT_EQUAL(bctbx_byte_to_char(0x0c), "c"[0], char, "%c"); BC_ASSERT_EQUAL(bctbx_byte_to_char(0x0e), "e"[0], char, "%c"); bctbx_str_to_uint8(outputBytes, a55aString, 4); BC_ASSERT_NSTRING_EQUAL((char *)outputBytes, (char *)a55aBytes, 2); bctbx_str_to_uint8(outputBytes, upString, 16); BC_ASSERT_NSTRING_EQUAL((char *)outputBytes, (char *)upBytes, 8); bctbx_str_to_uint8(outputBytes, downString, 16); BC_ASSERT_NSTRING_EQUAL((char *)outputBytes, (char *)downBytes, 8); bctbx_int8_to_str(outputString, a55aBytes, 2); BC_ASSERT_NSTRING_EQUAL((char *)outputString, (char *)a55aString, 4); bctbx_int8_to_str(outputString, upBytes, 8); BC_ASSERT_NSTRING_EQUAL((char *)outputString, (char *)upString, 16); bctbx_int8_to_str(outputString, downBytes, 8); BC_ASSERT_NSTRING_EQUAL((char *)outputString, (char *)downString, 16); bctbx_uint32_to_str(outputString, 0x5aa5c376); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "5aa5c376", 8); bctbx_uint32_to_str(outputString, 0x01234567); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "01234567", 8); bctbx_uint32_to_str(outputString, 0xfedcba98); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "fedcba98", 8); BC_ASSERT_EQUAL(bctbx_str_to_uint32((uint8_t *)"5aa5c376"), 0x5aa5c376, uint32_t, "0x%08x"); BC_ASSERT_EQUAL(bctbx_str_to_uint32((uint8_t *)"01234567"), 0x01234567, uint32_t, "0x%08x"); BC_ASSERT_EQUAL(bctbx_str_to_uint32((uint8_t *)"fedcba98"), 0xfedcba98, uint32_t, "0x%08x"); bctbx_uint64_to_str(outputString, 0xfa5c37643cde8de0); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "fa5c37643cde8de0", 16); bctbx_uint64_to_str(outputString, 0x0123456789abcdef); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "0123456789abcdef", 16); bctbx_uint64_to_str(outputString, 0xfedcba9876543210); BC_ASSERT_NSTRING_EQUAL((char *)outputString, "fedcba9876543210", 16); BC_ASSERT_EQUAL(bctbx_str_to_uint64((uint8_t *)"fa5c37643cde8de0"), 0xfa5c37643cde8de0, uint64_t, "0x%" PRIx64); BC_ASSERT_EQUAL(bctbx_str_to_uint64((uint8_t *)"0123456789abcdef"), 0x0123456789abcdef, uint64_t, "0x%" PRIx64); BC_ASSERT_EQUAL(bctbx_str_to_uint64((uint8_t *)"fedcba9876543210"), 0xfedcba9876543210, uint64_t, "0x%" PRIx64); } static void time_functions(void) { bctoolboxTimeSpec testTs; bctoolboxTimeSpec y2k,monday6Feb2017; y2k.tv_sec = 946684800; y2k.tv_nsec = 123456789; monday6Feb2017.tv_sec = 1486347823; monday6Feb2017.tv_nsec = 0; memcpy(&testTs, &y2k, sizeof(bctoolboxTimeSpec)); BC_ASSERT_EQUAL(bctbx_timespec_compare(&y2k, &testTs), 0, int, "%d"); bctbx_timespec_add(&testTs, 604800); BC_ASSERT_EQUAL(testTs.tv_sec, y2k.tv_sec+604800, int64_t, "%" PRIi64); BC_ASSERT_EQUAL(testTs.tv_nsec, y2k.tv_nsec, int64_t, "%" PRIi64); BC_ASSERT_TRUE(bctbx_timespec_compare(&y2k, &testTs)<0); memcpy(&testTs, &y2k, sizeof(bctoolboxTimeSpec)); bctbx_timespec_add(&testTs, -604800); BC_ASSERT_EQUAL(testTs.tv_sec, y2k.tv_sec-604800, int64_t, "%" PRIi64); BC_ASSERT_EQUAL(testTs.tv_nsec, y2k.tv_nsec, int64_t, "%" PRIi64); BC_ASSERT_TRUE(bctbx_timespec_compare(&y2k, &testTs)>0); memcpy(&testTs, &y2k, sizeof(bctoolboxTimeSpec)); bctbx_timespec_add(&testTs, -946684801); BC_ASSERT_EQUAL(testTs.tv_sec, 0, int64_t, "%" PRIi64); BC_ASSERT_EQUAL(testTs.tv_nsec, 0, int64_t, "%" PRIi64); /* test the get utc time function * there is no easy way to ensure we get the correct time, just check it is at least not the time from last boot, * check it is greater than the current time as this test was written(6feb2017) */ bctbx_get_utc_cur_time(&testTs); BC_ASSERT_TRUE(bctbx_timespec_compare(&testTs, &monday6Feb2017)>0); BC_ASSERT_EQUAL(bctbx_time_string_to_sec(NULL), 0, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec(""), 0, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("0"), 0, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("1500"), 1500, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("2500s"), 2500, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("10m"), 600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("5h"), 5*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("2d"), 2*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("3W"), 3*7*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("6M"), 6*30*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("7Y"), 7*365*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("7Y6M2W"), (7*365+6*30+2*7)*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("2m30"), 2*60+30, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("15d1M"), (15+1*30)*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("15d5z"), 15*24*3600, uint32_t, "%d"); BC_ASSERT_EQUAL(bctbx_time_string_to_sec("15dM12h"), (15*24+12)*3600, uint32_t, "%d"); } static void bctbx_addrinfo_sort_test(void) { struct addrinfo * res1 = bctbx_name_to_addrinfo(AF_INET6, SOCK_DGRAM, "sip3.linphone.org", 27256); struct addrinfo * res2 = bctbx_ip_address_to_addrinfo(AF_INET6, SOCK_DGRAM, "91.121.209.194", 27256); struct addrinfo * res3 = bctbx_ip_address_to_addrinfo(AF_INET, SOCK_DGRAM, "91.121.209.194", 27256); bool_t searching_for_v6=TRUE; struct addrinfo * ai = NULL; char printable_ip[256]; struct addrinfo * res = res3; for ( ai=res2 ; ai !=NULL; ai=ai->ai_next) { if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)(ai->ai_addr))->sin6_addr)) { res->ai_next=ai; break; } } res->ai_next->ai_next=res1; res->ai_next->ai_next->ai_next=NULL; //So now, res as ipv4 first, then v4 mapped, then v6 for ( ai=res ; ai !=NULL; ai=ai->ai_next) { bctbx_addrinfo_to_printable_ip_address(ai, printable_ip, sizeof(printable_ip)); bctbx_message("bctbx_getaddrinfo origin address:%s", printable_ip); } //now apply bctbx_addrinfo_sort for ( ai=bctbx_addrinfo_sort(res) ; ai !=NULL; ai=ai->ai_next) { if (ai->ai_family == AF_INET6) { if (!searching_for_v6) { BC_ASSERT_FALSE(IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)(ai->ai_addr))->sin6_addr)); } if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)(ai->ai_addr))->sin6_addr)) { searching_for_v6 = FALSE; // stating from now, all subsequent IP shall be pure V6 } } bctbx_addrinfo_to_printable_ip_address(ai, printable_ip, sizeof(printable_ip)); bctbx_message("bctbx_getaddrinfo sorted address:%s", printable_ip); } bctbx_freeaddrinfo(res); } static void bctbx_directory_utils_test(void) { // Create a directory in the writeable one char *tmpDirPath = bctbx_strdup_printf("%s/tmp_dir/", bc_tester_get_writable_dir_prefix()); // Check the directory does not exists bool_t tmpDirExists = bctbx_directory_exists(tmpDirPath); BC_ASSERT_FALSE(tmpDirExists); // This directory must not exists when performing this test, if it does, it means the test failed before, delete it manually if (tmpDirExists) return; // Create it BC_ASSERT_EQUAL(bctbx_mkdir(tmpDirPath), 0, int, "%d"); // Check it is there BC_ASSERT_TRUE(bctbx_directory_exists(tmpDirPath)); // Try to create 2 layers of subdirectory, it shall fail char *tmpDirPath2 = bctbx_strdup_printf("%s/a/b/", tmpDirPath); BC_ASSERT_FALSE(bctbx_directory_exists(tmpDirPath2)); BC_ASSERT_NOT_EQUAL(bctbx_mkdir(tmpDirPath2), 0, int, "%d"); BC_ASSERT_FALSE(bctbx_directory_exists(tmpDirPath2)); bctbx_free(tmpDirPath2); // Create a subdirectory tmpDirPath2 = bctbx_strdup_printf("%s/a/", tmpDirPath); BC_ASSERT_FALSE(bctbx_directory_exists(tmpDirPath2)); BC_ASSERT_EQUAL(bctbx_mkdir(tmpDirPath2), 0, int, "%d"); BC_ASSERT_TRUE(bctbx_directory_exists(tmpDirPath2)); // Create empty files in tmpDirPath and tmpDirPath2 char *filename1 = bctbx_strdup_printf("%s/filename1.txt", tmpDirPath); char *filename2 = bctbx_strdup_printf("%s/.filename2.txt", tmpDirPath); char *filename3 = bctbx_strdup_printf("%s/filename3", tmpDirPath2); bctbx_vfs_t* stdVfs = bctbx_vfs_get_standard(); bctbx_vfs_file_t *fp = bctbx_file_open(stdVfs, filename1, "w"); bctbx_file_close(fp); fp = bctbx_file_open(stdVfs, filename2, "w"); bctbx_file_close(fp); fp = bctbx_file_open(stdVfs, filename3, "w"); bctbx_file_close(fp); // Check the files exist BC_ASSERT_EQUAL(bctbx_file_exist(filename1), 0, int, "%d"); BC_ASSERT_EQUAL(bctbx_file_exist(filename2), 0, int, "%d"); BC_ASSERT_EQUAL(bctbx_file_exist(filename3), 0, int, "%d"); // Try to delete the directory non recursively, it shall fail BC_ASSERT_NOT_EQUAL(bctbx_rmdir(tmpDirPath, FALSE), 0, int, "%d"); // Delete using recursivity BC_ASSERT_EQUAL(bctbx_rmdir(tmpDirPath, TRUE), 0, int, "%d"); // Check it is not there anymore BC_ASSERT_FALSE(bctbx_directory_exists(tmpDirPath)); // cleaning bctbx_free(tmpDirPath); bctbx_free(tmpDirPath2); bctbx_free(filename1); bctbx_free(filename2); bctbx_free(filename3); } static test_t utils_tests[] = { TEST_NO_TAG("Bytes to/from Hexa strings", bytes_to_from_hexa_strings), TEST_NO_TAG("Time", time_functions), TEST_NO_TAG("Addrinfo sort", bctbx_addrinfo_sort_test), TEST_NO_TAG("Directory utils", bctbx_directory_utils_test) }; test_suite_t utils_test_suite = {"Utils", NULL, NULL, NULL, NULL, sizeof(utils_tests) / sizeof(utils_tests[0]), utils_tests}; bctoolbox-5.2.0/tester/vfs.c000066400000000000000000000340661434566643100160060ustar00rootroot00000000000000/* * Copyright (c) 2020 Belledonne Communications SARL. * * This file is part of bctoolbox. * * 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 "bctoolbox_tester.h" #include "bctoolbox/vfs.h" #include "bctoolbox/vfs_standard.h" #include "bctoolbox/logging.h" static char *patterns[] = { "this is a small pattern", "O brillant éclat de la lampe d'argile, commodément suspendue dans cet endroit accessible aux regards, nous ferons connaître ta naissance et tes aventures; façonnée par la course de la roue du potier, tu portes dans tes narines les splendeurs éclatantes du soleil: produis donc au dehors le signal de ta flamme, comme il est convenu. A toi seule notre confiance; et nous avons raison, puisque, dans nos chambres, tu honores de ta présence nos essais de postures aphrodisiaques: témoin du mouvement de nos corps, personne n'écarte ton oeil de nos demeures. Seule tu éclaires les cavités secrètes de nos aines, brûlant la fleur de leur duvet. Ouvrons-nous furtivement des celliers pleins de fruits ou de liqueur bachique, tu es notre confidente, et ta complicité ne bavarde pas avec les voisins. Aussi connaîtras-tu les desseins actuels, que j'ai formés, à la fête des Skira, avec mes amies. Seulement, nulle ne se présente de celles qui devaient venir. Cependant voici l'aube: l'assemblée va se tenir dans un instant, et il nous faut prendre place, en dépit de Phyromakhos, qui, s'il vous en souvient, disait de nous: «Les femmes doivent avoir des sièges séparés et à l'écart.» Que peut-il être arrivé? N'ont-elles pas dérobé les barbes postiches, qu'on avait promis d'avoir, ou leur a-t-il été difficile de voler en secret les manteaux de leurs maris? Ah! je vois une lumière qui s'avance: retirons-nous un peu, dans la crainte que ce ne soit quelque homme qui approche par ici." }; // convenience define for test buffer size #define F_SIZE 2*BCTBX_VFS_PRINTF_PAGE_SIZE #define G_SIZE BCTBX_VFS_GETLINE_PAGE_SIZE-1 void file_fprint_simple_test() { char in_buf[F_SIZE]; char out_buf[F_SIZE]; memset(in_buf, 0, F_SIZE); memset(out_buf, 0, F_SIZE); /* create a file */ char *path = bc_tester_file("vfs_fprintf_simple.txt"); remove(path); // make sure it does not exist bctbx_vfs_file_t *fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* small printf at offset 0 */ BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[0]) - strlen(patterns[0]) == 0); /* read it */ ssize_t readSize = bctbx_file_read(fp, out_buf, 100, 0); BC_ASSERT_EQUAL(readSize - strlen(patterns[0]), 0, int, "%d"); BC_ASSERT_TRUE(memcmp(patterns[0],out_buf, readSize) == 0); memset(out_buf, 0, F_SIZE); /* close the file, re-open and read it again */ BC_ASSERT_NOT_EQUAL(bctbx_file_close(fp), BCTBX_VFS_ERROR, int, "%d"); fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_EQUAL(readSize - strlen(patterns[0]), 0, int, "%d"); BC_ASSERT_TRUE(memcmp(patterns[0],out_buf, readSize) == 0); memset(out_buf, 0, F_SIZE); /* write a large quantity of small inputs - less than a cache page */ size_t inSize = 0; while (inSize < BCTBX_VFS_PRINTF_PAGE_SIZE/2) { sprintf(in_buf + inSize, "%s", patterns[0]); // build a buffer image of what we are writing in the file BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[0]) - strlen(patterns[0]) == 0); inSize+=strlen(patterns[0]); } /* read it (it flushes the write cache) */ readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_TRUE((readSize - inSize) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf, readSize) == 0); memset(out_buf, 0, F_SIZE); /* reset the file pointer, and write 3 times the whole half page buffer - more than a cache page */ bctbx_file_seek(fp, 0, SEEK_SET); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - inSize == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - inSize == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - inSize == 0); readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_TRUE((readSize - 3*inSize) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf, inSize) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf+inSize, inSize) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf+2*inSize, inSize) == 0); memcpy(in_buf, out_buf, BCTBX_VFS_PRINTF_PAGE_SIZE); // in_buf has now 1 cache page size in_buf[BCTBX_VFS_PRINTF_PAGE_SIZE] = '\0'; memset(out_buf, 0, F_SIZE); /* write exactly a page size in one write - the read flushed the cache, so we start we an empty one */ BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - BCTBX_VFS_PRINTF_PAGE_SIZE == 0); readSize = bctbx_file_read(fp, out_buf, F_SIZE, readSize); // offset a readSize, so we start reading at the end of what we read before BC_ASSERT_TRUE((readSize - BCTBX_VFS_PRINTF_PAGE_SIZE) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf, BCTBX_VFS_PRINTF_PAGE_SIZE) == 0); BC_ASSERT_TRUE(bctbx_file_size(fp) - 3*inSize - BCTBX_VFS_PRINTF_PAGE_SIZE == 0); memset(out_buf, 0, F_SIZE); /* delete file and reopen it */ BC_ASSERT_NOT_EQUAL(bctbx_file_close(fp), BCTBX_VFS_ERROR, int, "%d"); remove(path); fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* write a whole page - 1, then a small buffer, read it all */ in_buf[BCTBX_VFS_PRINTF_PAGE_SIZE-1] = '\0'; BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - BCTBX_VFS_PRINTF_PAGE_SIZE + 1 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[0]) - strlen(patterns[0]) == 0); readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_TRUE((readSize - BCTBX_VFS_PRINTF_PAGE_SIZE + 1 - strlen(patterns[0])) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf, BCTBX_VFS_PRINTF_PAGE_SIZE - 1) == 0); BC_ASSERT_TRUE(memcmp(patterns[0],out_buf + BCTBX_VFS_PRINTF_PAGE_SIZE - 1, strlen(patterns[0])) == 0); /* delete file and reopen it */ BC_ASSERT_NOT_EQUAL(bctbx_file_close(fp), BCTBX_VFS_ERROR, int, "%d"); remove(path); fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* write a small buffer and then a whole page - 1 */ BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[0]) - strlen(patterns[0]) == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", in_buf) - BCTBX_VFS_PRINTF_PAGE_SIZE + 1 == 0); readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_TRUE((readSize - BCTBX_VFS_PRINTF_PAGE_SIZE + 1 - strlen(patterns[0])) == 0); BC_ASSERT_TRUE(memcmp(patterns[0],out_buf, strlen(patterns[0])) == 0); BC_ASSERT_TRUE(memcmp(in_buf,out_buf + strlen(patterns[0]), BCTBX_VFS_PRINTF_PAGE_SIZE - 1) == 0); /* cleaning */ BC_ASSERT_NOT_EQUAL(bctbx_file_close(fp), BCTBX_VFS_ERROR, int, "%d"); remove(path); bctbx_free(path); } void file_fprint_and_write_test() { char out_buf[F_SIZE]; memset(out_buf, 0, F_SIZE); /* create a file */ char *path = bc_tester_file("vfs_fprintf_mixed.txt"); remove(path); // make sure it does not exist bctbx_vfs_file_t *fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* printf at offset 0 - fits in cache, so it won't make it to the file */ BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[1]) - strlen(patterns[1]) == 0); /* write at offset 0 too, expected to overwrite what was printed before */ BC_ASSERT_TRUE(bctbx_file_write(fp, patterns[0], strlen(patterns[0]), 0) - strlen(patterns[0]) == 0); /* read all and check */ ssize_t readSize = bctbx_file_read(fp, out_buf, F_SIZE, 0); BC_ASSERT_TRUE(readSize - strlen(patterns[1]) == 0); out_buf[strlen(patterns[1])] = '\0'; BC_ASSERT_TRUE(memcmp(out_buf, patterns[0], strlen(patterns[0])) == 0); BC_ASSERT_TRUE(memcmp(out_buf + strlen(patterns[0]), patterns[1]+strlen(patterns[0]), strlen(patterns[1]) - strlen(patterns[0])) == 0); /* cleaning */ BC_ASSERT_NOT_EQUAL(bctbx_file_close(fp), BCTBX_VFS_ERROR, int, "%d"); remove(path); bctbx_free(path); } void file_get_nxtline_test() { //char in_buf[F_SIZE]; char out_buf[2*G_SIZE]; //memset(in_buf, 0, F_SIZE); memset(out_buf, 0, 2*G_SIZE); /* create a file */ char *path = bc_tester_file("vfs_get_nxtline.txt"); remove(path); // make sure it does not exist bctbx_vfs_file_t *fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* write a test file using fprintf, alternate line ending with \n, \r or \r\n */ BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\n", patterns[0]) - strlen(patterns[0]) - 1 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\r", patterns[1]) - strlen(patterns[1]) -1 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\r\n", patterns[0]) - strlen(patterns[0]) -2 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\n", patterns[1]) - strlen(patterns[1]) -1 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "\n") - 1 == 0); //empty line /* parse all the lines */ bctbx_file_seek(fp, 0, SEEK_SET); // reset print/get file pointer BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[0]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[0], strlen(patterns[0])); BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[1]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[1], strlen(patterns[1])); BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[0]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[0], strlen(patterns[0])); BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[1]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[1], strlen(patterns[1])); BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - 1 == 0); /* cleaning */ bctbx_file_close(fp); remove(path); fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* write a file larger than a read cache page, with lines ending with \n */ size_t written = 0; int i,count = 0; while (written < BCTBX_VFS_GETLINE_PAGE_SIZE + 1000) { BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\n", patterns[0]) - strlen(patterns[0]) - 1 == 0); BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s\n", patterns[1]) - strlen(patterns[1]) -1 == 0); count++; written += strlen(patterns[0]) + strlen(patterns[1]) + 2; } /* parse it */ bctbx_file_seek(fp, 0, SEEK_SET); // reset print/get file pointer for (i=0; i< count; i++) { BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[0]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[0], strlen(patterns[0])); BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, G_SIZE) - strlen(patterns[1]) - 1 == 0); BC_ASSERT_NSTRING_EQUAL(out_buf, patterns[1], strlen(patterns[1])); } /* cleaning */ bctbx_file_close(fp); remove(path); fp = bctbx_file_open2(&bcStandardVfs, path, O_RDWR|O_CREAT); // open using standard vfs BC_ASSERT_PTR_NOT_NULL(fp); /* write a file with one line larger than read cache */ written = 0; count = 0; while (written < BCTBX_VFS_GETLINE_PAGE_SIZE + 1000) { BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "%s", patterns[1]) - strlen(patterns[1]) == 0); count++; written += strlen(patterns[1]); } BC_ASSERT_TRUE(bctbx_file_fprintf(fp, 0, "\n") - 1 == 0); //empty line /* parse it */ bctbx_file_seek(fp, 0, SEEK_SET); // reset print/get file pointer BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, BCTBX_VFS_GETLINE_PAGE_SIZE) - (BCTBX_VFS_GETLINE_PAGE_SIZE -1) == 0); // read the size of a page cache, there is no \n in it, but it cannot send more data than given buffer -1 bctbx_file_seek(fp, 0, SEEK_SET); // reset print/get file pointer BC_ASSERT_TRUE(bctbx_file_get_nxtline(fp, out_buf, 2*G_SIZE) - (written +1) == 0); // read more, we shall get a warning in the logs but the whole line in return for (i=0; i