diff options
148 files changed, 34374 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7565358 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +build/ +autoconf/ +doc/html/ +configure +Makefile +Makefile.in +aclocal.m4 +.kdev* +*.o +*.lo +*.la +*.authors +*.kdev4 +*.deps +*.libs +*.pc +*~ + @@ -0,0 +1,6 @@ +Authors of GNU ZRTP + +Werner.Dittmann@t-online.de + +Werner Dittmann designed and implimented GNU ZRTP based on Phil Zimmermann's +ZRTP specification. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..399e776 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,200 @@ +# Copyright (C) 2009 Werner Dittman +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +cmake_minimum_required(VERSION 2.6) + +PROJECT(libzrtpcpp) +set (VERSION 2.3.4) +set (PACKAGE libzrtpcpp) +set (SOVERSION ${VERSION}) +STRING(REGEX REPLACE "[.].*$" "" SOVERSION ${SOVERSION}) + +SET(CPACK_PACKAGE_VERSION_MAJOR ${SOVERSION}) +SET(CPACK_PACKAGE_VERSION_MINOR ${VERSION}) +SET(CPACK_PACKAGE_VERSION_PATCH ${VERSION}) +STRING(REGEX REPLACE "[.][0-9]*$" "" CPACK_PACKAGE_VERSION_MINOR ${VERSION}) +STRING(REGEX REPLACE ".*[.]" "" CPACK_PACKAGE_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR}) +STRING(REGEX REPLACE ".*[.]" "" CPACK_PACKAGE_VERSION_PATCH ${VERSION}) + +if(CMAKE_GENERATOR MATCHES "Unix Makefiles") + add_custom_target(cleandist + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}[-_]*.gz" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}_*.dsc" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}-*.rpm" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}[-_]*.deb" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}[-_]*.changes" + COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}-*.zip" + ) + + add_custom_target(dist + DEPENDS cleandist + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND git archive --format tar --output="${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}-${VERSION}.tar" --prefix="${PACKAGE}-${VERSION}/" HEAD + COMMAND gzip "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE}-${VERSION}.tar" + ) +endif() + +if(MSVC60) + set(BUILD_STATIC ON CACHE BOOL "static linking only" FORCE) + MARK_AS_ADVANCED(BUILD_STATIC) +else() + option(BUILD_STATIC "Set to OFF to build shared libraries" OFF) +endif() + +# set to true for debug and trace during CMakeLists development +set(CMAKE_VERBOSE_MAKEFILE FALSE) + +MESSAGE( STATUS "Configuring GNU ${PROJECT_NAME} ${VERSION}...") + +# include most of the fine stuff we need +include(cmake/Modules/FindGcryptConfig.cmake) +include(FindPkgConfig) +include(CheckLibraryExists) +include(CheckIncludeFiles) +include(cmake/Modules/AutoArgs.cmake) + +if(${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) + include(cmake/Modules/GeneratePackage.cmake) + + GENERATE_PACKAGING(${PACKAGE} ${VERSION}) +endif() + +# check the -Denable-ccrtp setting, defaults to true +enable_arg(ccrtp true "Enable GNU ccRTP support for GNU ZRTP") +args_help() + +if (NOT LIB_SUFFIX) + set(LIBDIRNAME "lib") + # this caused problems in debian where it has to always be lib.... + if (NOT EXISTS /etc/debian_version) + if ( "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" ) + set(LIBDIRNAME "lib64") + endif() + endif() +else() + set(LIBDIRNAME "lib${LIB_SUFFIX}") +endif() + +# setup the Thread include and lib +find_package(Threads) +if(CMAKE_HAVE_PTHREAD_H) + set(HAVE_PTHREAD_H TRUE) +endif() +set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) + +# define the name of the lib. zrtpcppcore does not include the ccRTP stuff. +set(zrtplib zrtpcppcore) +if(enable_ccrtp) + if (USES_CCRTP_INCLUDE_DIRS) + message(STATUS " Using local commoncpp dependency") + else() + find_package(PkgConfig) + pkg_check_modules(USES_CCRTP libccrtp>=2.0.0) + endif() + include_directories(${USES_CCRTP_INCLUDE_DIRS}) + link_directories(${USES_CRTP_LIBRARY_DIRS}) + add_definitions(${USES_CCRTP_CFLAGS}) + set (LIBS ${LIBS} ${USES_CCRTP_LDFLAGS} ${USES_CCRTP_LIBRARIES}) + set(zrtplib zrtpcpp) +endif() + +# now get info about crypto libraries +pkg_check_modules(OPENSSL libcrypto>=0.9.8) +if (OPENSSL_FOUND) + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${OPENSSL_INCLUDE_DIRS}) #update include files search directory + check_include_files(openssl/bn.h HAVE_OPENSSL_BN_H) + check_include_files(openssl/aes.h HAVE_OPENSSL_AES_H) + check_include_files(openssl/sha.h HAVE_OPENSSL_SHA_H) + check_include_files(openssl/ec.h HAVE_OPENSSL_EC_H) + check_library_exists(crypto EVP_CipherInit_ex "${OPENSSL_LIBDIR}" HAVE_SSL_CRYPT) #use search lib directory from pkg-config + set(LIBS ${LIBS} -lcrypto) + set(CRYPTOBACKEND "libcrypto >= 0.9.8") + set(BUILD_REQ "libopenssl-devel >= 0.9.8") + set(PACKAGE_REQ "libopenssl >= 0.9.8") + include_directories(${OPENSSL_INCLUDE_DIRS}) #update includes directory from pkg-config +endif() + +if(NOT HAVE_OPENSSL_EC_H) + gcr_check(GCRYPT gcrypt) + if(GCRYPT_FOUND) + check_include_files(gcrypt.h HAVE_GCRYPT_H) + set(LIBS ${LIBS} ${GCRYPT_LIBRARIES}) + set(BUILD_REQ "libgcrypt-devel") + set(CRYPTOBACKEND="") + set(PACKAGE_REQ "libgcrypt") + endif() +endif() + +if(NOT OPENSSL_FOUND AND NOT GCRYPT_FOUND) + message(FATAL_ERROR "No crypto library found") +endif() + +check_include_files(stdlib.h HAVE_STDLIB_H) +check_include_files(string.h HAVE_STRING_H) + +# necessary and required modules checked, ready to generate config.h +configure_file(libzrtpcpp-config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/libzrtpcpp-config.h) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# the following set(...) commands are only to have backward +# compatibility with autoconf stuff to generate the pc file +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix ${prefix}/bin) +set(libdir ${prefix}/${LIBDIRNAME}) +set(includedir ${prefix}/include) +set(PACKAGE pkgconfig) +configure_file(libzrtpcpp.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/lib${zrtplib}.pc @ONLY) + +configure_file(libzrtpcpp.spec.cmake ${CMAKE_CURRENT_BINARY_DIR}/libzrtpcpp.spec @ONLY) + +#to make sure includes are first taken from those directory +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) + +add_definitions(-g -O2 -fno-strict-aliasing) +if(CMAKE_COMPILER_IS_GNUCXX) + add_definitions(-Wno-long-long -Wno-char-subscripts) + add_definitions(-Wall -pedantic) + add_definitions(-DNEW_STDCPP) +endif() + +add_subdirectory(src) + +if (enable_ccrtp) + add_subdirectory(demo) +endif() + +if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/package/) + MESSAGE(STATUS "package dir not found") + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/package/) +endif() + +########### install files ############### +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${zrtplib}.pc DESTINATION ${LIBDIRNAME}/pkgconfig) + +if(${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) + + ########### Add uninstall target ############### + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + add_custom_target(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + +endif() +##very usefull for macosx, specially when using gtkosx bundler +if(APPLE) + if (NOT CMAKE_INSTALL_NAME_DIR) + set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "CMAKE_INSTALL_NAME_DIR set for macosx" ) + endif (NOT CMAKE_INSTALL_NAME_DIR) +endif(APPLE) + + @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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 + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7e78a60 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,26 @@ +2.3.4: + +- Fixes vulnerabilities found and reported by Mark Dowd +- Additional fix for nounce header size + +2.3.3: + +- fallback to gcrypt if no openssl elliptical support + +2.3.1: + +- 2.3.0 paranoid mode +- mingw build fixes +- MANY other fixes!! + +1.5.2: + +- a C wrapper was added to enable C programs to use GNU ZRTP +- some fixes in the code (race condition solved) +- better support of multi-stream mode +- change the old cxx file extension to cpp, some build system don't + like the old cxx (Android NDK for example) +- and much more + +-- +Add CMake configuration files. @@ -0,0 +1,22 @@ + +=== Configure, build and install GNU ZRTP 1.4.x === +After uninstalling the prevoius version you can build and install the +new version. Building and installation of the GNU ZRTP 1.1.0 uses the +usual triplet of ''./configure; make; sudo make install''. By default +GNU ZRTP install its files into the ''/usr/local'' hierarchy. Don't +forget to run ''/sbin/ldconfig'' if you use the ''make install''. + +Some distributions may offer GNU ZRTP packets and it is usally much +simpler and more safe to use the preconfigured packets than to build +the software from the source. Don't forget to install the +''developer'' packets if you plan to develop or if you already +developed own application that use GNU ZRTP. + +If you need to re-create the ''configure'' and the Makefiles then just +use your system's ''autoreconf'' command to re-create these files. No +specific reconfiguration script is necessary. + +After installation of GNU ZRTP you need to adapt your applications and +re-compile and re-build them. + + @@ -0,0 +1,238 @@ +== GNU ZRTP 2.0.0 == + +Modify some files to use the new uCommon/commoncpp libraries instead +of the GNU CC++ commoncpp2. This affects the ccRTP depended modules +such as ZrtpQueue and the Timeout stuff. + +Updated to version 2.0.0 to be in synch with the ccRTP version number +scheme. + + +== GNU ZRTP 1.6.0 == + +This version implements the Elliptic Curve Diffie-Helman (ECDH) +public-key algorithm. + +ZRTP also supports new algorithms which are defined as optional +in the ZRTP RFC. These are: +- Skein Hash +- Skein MAC for authentication +- Twofish symmetric ciphers + +Twofish ciphers and Skein MAC are supported by GNU ccRTP SRTP +implmentation as well. + + +== GNU ZRTP 1.5.4 == + +The changes in this release affect the ZRTP Configure mechanism only. +Some housekeeping stuff (desctructors) was added and the C Wrapper +how support ZRTP configure as well. + +Because of some API changes (added destructors) clients must be compiled +and linked with the new library. + + +== GNU ZRTP 1.5.2 == + +Quite a lot of enhancements: +- a CMake based build process was added +- a C wrapper was added to enable C programs to use GNU ZRTP +- some fixes in the code (race condition solved) +- better support of multi-stream mode +- change the old cxx file extension to cpp, some build system don't + like the old cxx (Android NDK for example) +- and much more + +Because of API changes clients must be compiled and linked with the new +library. + +== GNU ZRTP 1.5.0 == + +Adds a first version of a ZrtpConfigure class that provides applications +to select which crypto and hash methods to use. + +Because of API changes clients must be compiled and linked with the new +library. + +== GNU ZRTP 1.4.5 == + +Modify the Hello repeat timer handling to accomodate slow connections and/or +slow devices. + +Fix a problem when the other party sends only ZRTP packets at the beginning +of a RTP session. + +=== Interface changes in 1.4.5 === + +No external interfaces were changed, external API and ABI remain stable. +Internal interface modifications only to implement Ping/PingAck handling. + + +== GNU ZRTP 1.4.4 == + +Implement the Ping/PingAck packets and associated protocol extensions +as defined in [http://tools.ietf.org/html/draft-zimmermann-avt-zrtp-15]. + +=== Interface changes in 1.4.4 === + +No external interfaces were changed, external API and ABI remain stable. +Internal interface modifications only to implement Ping/PingAck handling. + + +== GNU ZRTP 1.4.2 == + +Introduce the Key Derivation Function (KDF) as defined in +[http://tools.ietf.org/html/draft-zimmermann-avt-zrtp-12]. + +The ZRTP protocol version was updated to 1.10. + +=== Interface changes in 1.4.2 === + +No interfaces were changed, API and ABI remain stable. + + +== GNU ZRTP 1.4.0 == + +This is the first release that is conformant to the ZRTP specification +that eventually will become a RFC. See: +[http://tools.ietf.org/html/draft-zimmermann-avt-zrtp-10] + +The ZRTP protocol version was updated to 1.00. + + +=== Interface changes in 1.4.0 === + +The ZrtpQueue and ZRtp classes implement a new method to get the other +party's ZID (ZRTP identifier). An application, for example a SIP or XMPP +client, may use this method to get the other party's ZID and store it +together in a contact list. This enable the application to check the ZID +if the user calls the other party again. A client shall implement such +a feature to enhance security if user's don't compare the SAS on every +call after they confirmed a SAS once. + +Clients must be compiled and linked with the new library. + + +== GNU ZRTP 1.3.1 == + +This is an update to version 1.3.0 and implements the ZRTP multi-stream +mode handshake. The ZRTP protocl version was updated to 0.90 and +interoperability tests using the latest Zfone build and Zfone Beta +(dated September 6, 2008) were successful. + +No changes in the external API and ABI with respect to 1.3.0 - thus no +recompile or rebuild of clients are necessary if they use 1.3.0. + +To checkout version 1.3.1 specify revision 494 (-r 494). + + +== GNU ZRTP 1.3.0 == + +This version is and update to version 1.1.0 an implements the latest +changes define in the ZRTP draft. The changes resulted in an update of the +API, therefore existing applications must be recompiled. + +This version of GNU ZRTP is compatible to and was tested to work with +the latest Zfone beta (dated June, 10, see Zfone project site). Only +in one specific error case is a slight incompatibility that will be +fixed with the next Zfone beta. This incompatibility results in a +severe error information at the client. The error only happens if +someone modified the first retained shared secret entry in the +retained secret cache, for example disk/storage read error. This is +a very unlikely situation. + +=== Interface changes in Version 1.3.0 === + +The Method ''setSipsSecret(...)'' is no longer available. ZRTP does +not support this additional secret anymore. + +The method ''setOtherSecret(...)'' was renamed to ''setPbxSecret(...)'' +to reflect the modification in the draft. + +The methos ''setSrtpsSecret(...)'' is was renamed to ''setAuxSecret(...)'' +to reflect the modification in the draft. + + +== GNU ZRTP 1.1.0 == + +GNU ZRTP 1.1.0 implements the basic ZRTP as specificied in the document +''draft-zimmermann-avt-zrtp-06x''. You may access this at this URL: +[http://zfoneproject.com/zrtp_ietf.html] + +This version of GNU ZRTP does not support the additiona featur of ZRTP +such as Multi-stream mode, Pre-shared mode, PBX enrollement, and SAS +Signature. However, to keep the external interface as stable as +possible I already implmented stubs for the additional features. Some +later versions may have these features implemented, depending if they +are required by the community. + +The current version of GNU ZRTP is compatible and was tested to work +with the latest Zfone beta (dated April, 2nd) (see Zfone project +site). + +=== Interface changes == + +The ''SymmetricZRTPSession'' implements some new methods to control +ZRTP and its new features. An application usually uses only a few +methods to setup GNU ZRTP. All others are optional and an application +may use them only if it requires a special feature (which are not yet +implemented :-) ). + +The ''ZrtpUserCallback'' class was modified as well. From an +application's point of view + + * The methods in ''ZrtpUserCallback'' are not pure virtual anymore + but just virtual and have a default implementation, usually a + simple return. An application may extend this class and overwrite + only those methods it requires. + + * Change of the constructor - remove the queue parameter thus we have + a very simple standard constructor. This modifcation may requires a + small change in the application or class that uses or extends + ''ZrtpUserCallback''. + + * The method showSAS has an additional parameter: + + showSAS(std::string sas, bool verified); + + the verified flag is set to true in SAS is verified, false if not verified. + This allows a more flexible support to display the SAS even if SAS is + verified. Formerly ZRTP did not call "showSAS()" if SAS was verified. Now + ZRTP always calls showSAS and provides the verification information + explicitly. + +* The siganture of the following user callback methods was changed: + + showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) + + zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, + int32_t subCode) + + The GNU ZRTP core and the ZRTP ccRTP extension do not contain + message strings anymore. Both use codes to inform an application + about events, problems or failure. The folder ''demo'' contains a + small demo program that shows one way how to map the codes to + strings. Delegating string handling and formating to the application + simplifies internationalization etc. + +Plaese note: some new callback methods and ''SymmetricZRTPSession'' +methods are only stubs in the currect version. The real implementation +(filling the stubs with real code) will be done some time later (see +above about unsupported features). + +=== Header files === + +The new version greatly reduces the number of header files installed +in the include directory. In the new version I decoupled the internal +header files and implementation from the external classes and +interfaces an application requires. Only six header files are +installed in GNU ZRTP's include directory (libzrtpcpp subdirectory in +the usual include paths) + +== Demo program == + +The new folder ''demo'' contains a small demo program that shows +various ways how to use GNU ZRTP to setup secure RTP (SRTP) sessions +even without signaling protocols + diff --git a/README.md b/README.md new file mode 100644 index 0000000..cac0592 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +## GNU ZRTP C++ + +This package provides a library that adds ZRTP support to the GNU +ccRTP stack. Phil Zimmermann developed ZRTP to allow ad-hoc, easy to +use key negotiation to setup Secure RTP (SRTP) sessions. GNU ZRTP works +together with GNU ccRTP (1.5.0 or later) and provides a ZRTP +implementation that can be directly embedded into client and server +applications. + +The GNU ZRTP implementation is compliant to [RFC 6189][]. Currently GNU ZRTP +C++ supports the following features: + +* multi-stream mode +* Finite field Diffie-Helman with 2048 and 3072 bit primes +* Elliptic curve Diffie-Helman with 256 and 384 bit curves +* AES-128 and AES-256 symmetric cipher +* Twofish-128 and Twofish-256 bit symmetric ciphers +* The SRTP authentication methods HMAC-SHA1 with 32 bit and 80 bit length and + the Skein MAC with 32 bit and 64 bit length +* The Short Authentication String (SAS) type with base 32 encoding (4 + characters) + +Enhanced features like PBX SAS relay aka *trusted Man-in-the-Middle* or +preshared mode are not supported but the GNU ZRTP C++ implementation defines +the necessary external interfaces and functions for these enhanced features +(stubs only). + +### Interoperability +During the development of ZRTP and its sister implementation ZRTP4J (the Java +version of the ZRTP) Phil Zimmermann, his developers, and I worked together to +make sure Phil's [Zfone][] implementation and the GNU ZRTP implementations can +work together. + +[zfone]: http://zfoneproject.com/index.html + + +### Other implementations based on GNU ZRTP C++ + +The ZRTP4J implementation is a copycat of the original C++ code. I used the +same overall class structure and copied a lot of C++ functionality to Java. Of +course some Java adaptation were done, for example to overcome the problem of +non-existing pointers :-), thus I use some non-obvious array handling. If you +are interessted in the Java implementation of ZRTP then you may have a look +[here][javazrtp]. The Jitsi project uses the Java implementation. Jitsi is a +powerfull communication client and is definitely worth a [look][jitsi]. + +To enable C based code to use ZRTP C++ I did a C wrapper that offers the same +functionality to C based RTP implementations. The first use of the ZRTP C +wrapper was for the [PJSIP][] library, actually the RTP part of this +library. The ZRTP handler for PJSIP is [here][pjzrtp]. This port enables PJSIP +based clients to use ZRTP. One of the first clients that use this feature is +*[CSipSimple][]*, an very good open source Android SIP client. + +[pjsip]: http://www.pjsip.org +[pjzrtp]: https://github.com/wernerd/ZRTP4PJ +[javazrtp]: https://github.com/wernerd/ZRTP4J +[jitsi]: http://www.jitsi.org +[csipsimple]: http://code.google.com/p/csipsimple + + +### Some notes on GNU ZRTP C++ history +The first application that demonstrated the embedded ZRTP was Minisp (now +defunct). Minisip has it's own RTP stack and the very first version of this +embedded ZRTP implementation worked together with this specific RTP stack. + +A few weeks later I implemented the GNU ccRTP glue code and ZRTP became part +of the official GNU ccRTP project and was named GNU ZRTP C++. The Twinkle +softphone uses GNU ccRTP and GNU ZRTP C++ since it's 0.8.2 release and Michel +de Boer, the implementor of Twinkle, created a nice user interface. All +following versions of Twinkle include GNU ZRTP C++ as well. + + +### License and further information +Please note, this library is licensed under the GNU GPL, version 3 or +later, and has been copyright assigned to the Free Software Foundation. + +For further information refer to the [ZRTP FAQ][zrtpfaq] and the +[GNU ZRTP howto][zrtphow]. Both are part of the GNU Telephony wiki and are +located in its documentation category. + +[zrtphow]: http://www.gnutelephony.org/index.php/GNU_ZRTP_How_To +[zrtpfaq]: http://www.gnutelephony.org/index.php/ZRTP_FAQ +[rfc 6189]: http://tools.ietf.org/html/rfc6189 + +## Building GNU ZRTP C++ +Since version 1.6 GNU ZRTP C++ supports the *cmake* based build process +only. The cmake build process is simpler than the GNU automake/autoconf +process. To build GNU ZRTP C++ perform the following steps after you unpacked +the source archive or pulled the source from [Github][]: + + cd <zrtpsrc_dir> + mkdir build + cd build + cmake .. + make + +Running cmake in a separate `build` directory is the preferred way. Cmake and +the following `make` generate all files in or below the build directory. Thus +the base directory and the source directories are not polluted with `*.o`, +`*.la`, or other files that result from the build process. You may delete the +build directory and create a new one to start from fresh (this is the ultimate +`make clean` :-) ) or you may create a second directory to build with +different settings without mixing the two builds. + +[github]: http://github.com/wernerd/ZRTPCPP diff --git a/cmake/Modules/AutoArgs.cmake b/cmake/Modules/AutoArgs.cmake new file mode 100644 index 0000000..6f2ce54 --- /dev/null +++ b/cmake/Modules/AutoArgs.cmake @@ -0,0 +1,39 @@ +macro (enable_arg _enable_name _enable_default) +if (${ARGC} GREATER 2) + set(_auto_arg_help + ${_AUTO_ARG_HELP} + "-Denable-${_enable_name}=[true|false] -- ${ARGV2} (default: ${_enable_default})" + ) +endif() + +if(NOT DEFINED enable-${_enable_name} AND NOT DEFINED disable-${_enable_name}) + set(enable_${_enable_name} ${_enable_default}) +elseif(DEFINED enable-${_enable_name}) + if(enable-${_enable_name}) + set(enable_${_enable_name} true) + else() + set(enable_${_enable_name} false) + endif() + unset(enable-${_enable_name}) + unset(enable-${_enable_name} CACHE) +elseif(DEFINED disable-${_enable_name}) + if(disable-${_enable_name}) + set(enable_${_enable_name} false) + else() + set(enable_${_enable_name} true) + endif() + unset(disable-${_enable_name}) + unset(disable-${_enable_name} CACHE) +endif() +endmacro() + +macro(args_help) +if(DEFINED help-args) + message("Control arguments:") + foreach(_args_help ${_AUTO_ARG_HELP}) + message(${_args_help}) + endforeach() + unset(help-args) + unset(help-args CACHE) +endif() +endmacro() diff --git a/cmake/Modules/FindGcryptConfig.cmake b/cmake/Modules/FindGcryptConfig.cmake new file mode 100755 index 0000000..1770241 --- /dev/null +++ b/cmake/Modules/FindGcryptConfig.cmake @@ -0,0 +1,241 @@ +# - a gcrypt-config module for CMake
+#
+# Usage:
+# gcrypt_check(<PREFIX> [REQUIRED] <MODULE>)
+# checks if gcrypt is avialable
+#
+# When the 'REQUIRED' argument was set, macros will fail with an error
+# when gcrypt could not be found.
+#
+# It sets the following variables:
+# GCRYPT_CONFIG_FOUND ... true if libgcrypt-config works on the system
+# GCRYPT_CONFIG_EXECUTABLE ... pathname of the libgcrypt-config program
+# <PREFIX>_FOUND ... set to 1 if libgcrypt exist
+# <PREFIX>_LIBRARIES ... the libraries
+# <PREFIX>_CFLAGS ... all required cflags
+# <PREFIX>_ALGORITHMS ... the algorithms that this libgcrypt supports
+# <PREFIX>_VERSION ... gcrypt's version
+#
+# Examples:
+# gcrypt_check (GCRYPT gcrypt)
+# Check if a version of gcrypt is available, issues a warning
+# if not.
+#
+# gcrypt_check (GCRYPT REQUIRED gcrypt)
+# Check if a version of gcrypt is available and fails
+# if not.
+#
+# gcrypt_check (GCRYPT gcrypt>=1.4)
+# requires at least version 1.4 of gcrypt and defines e.g.
+# GCRYPT_VERSION=1.4.4. Issues a warning if a lower version
+# is available only.
+#
+# gcrypt_check (GCRYPT REQUIRED gcrypt>=1.4.4)
+# requires at least version 1.4.4 of gcrypt and fails if
+# only gcrypt 1.4.3 or lower is available only.
+#
+
+# Copyright (C) 2010 Werner Dittmann <werner.dittmann@t-online.de>
+#
+# Redistribution and use, with or without modification, are permitted
+# provided that the following conditions are met:
+#
+# 1. Redistributions must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# 2. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior
+# written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# This is a much edited and simplified variant of the original UsePkgConfig.cmake
+# from Enrico Scholz
+# Copyright (C) 2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+#
+
+### Common stuff ####
+set(GCR_CONFIG_VERSION 1)
+set(GCR_CONFIG_FOUND 0)
+
+find_program(GCR_CONFIG_EXECUTABLE NAMES libgcrypt-config --version DOC "libgcrypt-config executable")
+mark_as_advanced(GCR_CONFIG_EXECUTABLE)
+
+if(GCR_CONFIG_EXECUTABLE)
+ set(GCR_CONFIG_FOUND 1)
+endif(GCR_CONFIG_EXECUTABLE)
+
+
+# Unsets the given variables
+macro(_gcrconfig_unset var)
+ set(${var} "" CACHE INTERNAL "")
+endmacro(_gcrconfig_unset)
+
+macro(_gcrconfig_set var value)
+ set(${var} ${value} CACHE INTERNAL "")
+endmacro(_gcrconfig_set)
+
+# Invokes libgcrypt-config, cleans up the result and sets variables
+macro(_gcrconfig_invoke _gcrlist _prefix _varname _regexp)
+ set(_gcrconfig_invoke_result)
+
+ execute_process(
+ COMMAND ${GCR_CONFIG_EXECUTABLE} ${ARGN}
+ OUTPUT_VARIABLE _gcrconfig_invoke_result
+ RESULT_VARIABLE _gcrconfig_failed)
+
+ if (_gcrconfig_failed)
+ set(_gcrconfig_${_varname} "")
+ _gcrconfig_unset(${_prefix}_${_varname})
+ else(_gcrconfig_failed)
+ string(REGEX REPLACE "[\r\n]" " " _gcrconfig_invoke_result "${_gcrconfig_invoke_result}")
+ string(REGEX REPLACE " +$" "" _gcrconfig_invoke_result "${_gcrconfig_invoke_result}")
+
+ if (NOT ${_regexp} STREQUAL "")
+ string(REGEX REPLACE "${_regexp}" " " _gcrconfig_invoke_result "${_gcrconfig_invoke_result}")
+ endif(NOT ${_regexp} STREQUAL "")
+
+ separate_arguments(_gcrconfig_invoke_result)
+
+ #message(STATUS " ${_varname} ... ${_gcrconfig_invoke_result}")
+ set(_gcrconfig_${_varname} ${_gcrconfig_invoke_result})
+ _gcrconfig_set(${_prefix}_${_varname} "${_gcrconfig_invoke_result}")
+ endif(_gcrconfig_failed)
+endmacro(_gcrconfig_invoke)
+
+macro(_gcrconfig_invoke_dyn _gcrlist _prefix _varname cleanup_regexp)
+ _gcrconfig_invoke("${_gcrlist}" ${_prefix} ${_varname} "${cleanup_regexp}" ${ARGN})
+endmacro(_gcrconfig_invoke_dyn)
+
+# Splits given arguments into options and a package list
+macro(_gcrconfig_parse_options _result _is_req)
+ set(${_is_req} 0)
+
+ foreach(_gcr ${ARGN})
+ if (_gcr STREQUAL "REQUIRED")
+ set(${_is_req} 1)
+ endif (_gcr STREQUAL "REQUIRED")
+ endforeach(_gcr ${ARGN})
+
+ set(${_result} ${ARGN})
+ list(REMOVE_ITEM ${_result} "REQUIRED")
+endmacro(_gcrconfig_parse_options)
+
+###
+macro(_gcr_check_modules_internal _is_required _is_silent _prefix)
+ _gcrconfig_unset(${_prefix}_FOUND)
+ _gcrconfig_unset(${_prefix}_VERSION)
+ _gcrconfig_unset(${_prefix}_PREFIX)
+ _gcrconfig_unset(${_prefix}_LIBDIR)
+ _gcrconfig_unset(${_prefix}_LIBRARIES)
+ _gcrconfig_unset(${_prefix}_CFLAGS)
+ _gcrconfig_unset(${_prefix}_ALGORITHMS)
+
+ # create a better addressable variable of the modules and calculate its size
+ set(_gcr_check_modules_list ${ARGN})
+ list(LENGTH _gcr_check_modules_list _gcr_check_modules_cnt)
+
+ if(GCR_CONFIG_EXECUTABLE)
+ # give out status message telling checked module
+ if (NOT ${_is_silent})
+ message(STATUS "checking for module '${_gcr_check_modules_list}'")
+ endif(NOT ${_is_silent})
+
+ # iterate through module list and check whether they exist and match the required version
+ foreach (_gcr_check_modules_gcr ${_gcr_check_modules_list})
+
+ # check whether version is given
+ if (_gcr_check_modules_gcr MATCHES ".*(>=|=|<=).*")
+ string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\1" _gcr_check_modules_gcr_name "${_gcr_check_modules_gcr}")
+ string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\2" _gcr_check_modules_gcr_op "${_gcr_check_modules_gcr}")
+ string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\3" _gcr_check_modules_gcr_ver "${_gcr_check_modules_gcr}")
+ else(_gcr_check_modules_gcr MATCHES ".*(>=|=|<=).*")
+ set(_gcr_check_modules_gcr_name "${_gcr_check_modules_gcr}")
+ set(_gcr_check_modules_gcr_op)
+ set(_gcr_check_modules_gcr_ver)
+ endif(_gcr_check_modules_gcr MATCHES ".*(>=|=|<=).*")
+
+ set(_gcr_check_prefix "${_prefix}")
+
+ _gcrconfig_invoke(${_gcr_check_modules_gcr_name} "${_gcr_check_prefix}" VERSION "" --version )
+# _gcrconfig_invoke(${_gcr_check_modules_gcr_name} "${_gcr_check_prefix}" PREFIX "" --prefix )
+ _gcrconfig_invoke(${_gcr_check_modules_gcr_name} "${_gcr_check_prefix}" LIBRARIES "" --libs )
+ _gcrconfig_invoke(${_gcr_check_modules_gcr_name} "${_gcr_check_prefix}" CFLAGS "" --cflags )
+ _gcrconfig_invoke(${_gcr_check_modules_gcr_name} "${_gcr_check_prefix}" ALGORITHMS "" --algorithms )
+
+ message(STATUS " found ${_gcr_check_modules_gcr}, version ${_gcrconfig_VERSION}")
+ # handle the operands
+ set(_gcr_wrong_version 0)
+ if (_gcr_check_modules_gcr_op STREQUAL ">=")
+ if(_gcr_check_modules_gcr_ver VERSION_EQUAL _gcrconfig_VERSION)
+ message(STATUS " gcrypt wrong version: required: ${_gcr_check_modules_gcr_op}${_gcr_check_modules_gcr_ver}, found: ${_gcrconfig_VERSION}")
+ set(_gcr_wrong_version 1)
+ endif()
+
+ if(_gcrconfig_VERSION VERSION_LESS _gcr_check_modules_gcr_ver )
+ message(STATUS " gcrypt wrong version: required: ${_gcr_check_modules_gcr_op}${_gcr_check_modules_gcr_ver}, found: ${_gcrconfig_VERSION}")
+ set(_gcr_wrong_version 1)
+ endif()
+ endif(_gcr_check_modules_gcr_op STREQUAL ">=")
+
+ if (_gcr_check_modules_gcr_op STREQUAL "=")
+ if(_gcr_check_modules_gcr_ver VERSION_EQUAL _gcrconfig_VERSION)
+ message(STATUS " gcrypt wrong version: required: ${_gcr_check_modules_gcr_op}${_gcr_check_modules_gcr_ver}, found: ${_gcrconfig_VERSION}")
+ set(_gcr_wrong_version 1)
+ endif()
+ endif(_gcr_check_modules_gcr_op STREQUAL "=")
+
+ if (_gcr_check_modules_gcr_op STREQUAL "<=")
+ if(_gcr_check_modules_gcr_ver VERSION_EQUAL _gcrconfig_VERSION)
+ message(STATUS " gcrypt wrong version: required: ${_gcr_check_modules_gcr_op}${_gcr_check_modules_gcr_ver}, found: ${_gcrconfig_VERSION}")
+ set(_gcr_wrong_version 1)
+ endif()
+
+ if(_gcrconfig_VERSION VERSION_GREATER _gcr_check_modules_gcr_ver)
+ message(STATUS " gcrypt wrong version: required: ${_gcr_check_modules_gcr_op}${_gcr_check_modules_gcr_ver}, found: ${_gcrconfig_VERSION}")
+ set(_gcr_wrong_version 1)
+ endif()
+ endif(_gcr_check_modules_gcr_op STREQUAL "<=")
+ if (${_is_required} AND _gcr_wrong_version)
+ message(FATAL_ERROR "")
+ endif()
+
+ endforeach(_gcr_check_modules_gcr)
+ _gcrconfig_set(${_prefix}_FOUND 1)
+
+ else(GCR_CONFIG_EXECUTABLE)
+ if (${_is_required})
+ message(FATAL_ERROR "libgcrypt-config tool not found")
+ endif (${_is_required})
+ endif(GCR_CONFIG_EXECUTABLE)
+endmacro(_gcr_check_modules_internal)
+
+###
+### User visible macro starts here
+###
+
+###
+macro(gcr_check _prefix _module0)
+ # check cached value
+ if (NOT DEFINED __gcr_config_checked_${_prefix} OR __gcr_config_checked_${_prefix} LESS ${GCR_CONFIG_VERSION} OR NOT ${_prefix}_FOUND)
+ _gcrconfig_parse_options (_gcr_modules _gcr_is_required "${_module0}" ${ARGN})
+ _gcr_check_modules_internal("${_gcr_is_required}" 0 "${_prefix}" ${_gcr_modules})
+
+ _gcrconfig_set(__gcr_config_checked_${_prefix} ${GCR_CONFIG_VERSION})
+ endif(NOT DEFINED __gcr_config_checked_${_prefix} OR __gcr_config_checked_${_prefix} LESS ${GCR_CONFIG_VERSION} OR NOT ${_prefix}_FOUND)
+endmacro(gcr_check)
+
+###
+
+### Local Variables:
+### mode: cmake
+### End:
diff --git a/cmake/Modules/GeneratePackage.cmake b/cmake/Modules/GeneratePackage.cmake new file mode 100644 index 0000000..43a3d11 --- /dev/null +++ b/cmake/Modules/GeneratePackage.cmake @@ -0,0 +1,75 @@ + +MACRO(GENERATE_PACKAGING PACKAGE VERSION) + + # The following components are regex's to match anywhere (unless anchored) + # in absolute path + filename to find files or directories to be excluded + # from source tarball. + SET (CPACK_SOURCE_IGNORE_FILES + #svn files + "\\\\.svn/" + "\\\\.cvsignore$" + # temporary files + "\\\\.swp$" + # backup files + "~$" + # eclipse files + "\\\\.cdtproject$" + "\\\\.cproject$" + "\\\\.project$" + "\\\\.settings/" + "\\\\.kdev4/" + "\\\\.kdev4$" + "\\\\.kdev4_include_paths$" + # others + "\\\\.#" + "/#" + "/build/" + "/autom4te\\\\.cache/" + "/_build/" + "/doc/html/" + "/\\\\.git/" + # used before + "/CVS/" + "/\\\\.libs/" + "/\\\\.deps/" + "\\\\.o$" + "\\\\.lo$" + "\\\\.la$" + "\\\\.sh$" + "Makefile\\\\.in$" + ) + + SET(CPACK_PACKAGE_VENDOR "Werner Dittmann") + #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt") + #SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt") + #SET(CPACK_PACKAGE_VERSION_MAJOR ${version_major}) + #SET(CPACK_PACKAGE_VERSION_MINOR ${version_minor}) + #SET(CPACK_PACKAGE_VERSION_PATCH ${version_patch}) + SET( CPACK_GENERATOR "TBZ2") + SET( CPACK_SOURCE_GENERATOR "TBZ2") + SET( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" ) + INCLUDE(CPack) + +# SPECFILE() + + ADD_CUSTOM_TARGET( svncheck + COMMAND cd $(CMAKE_SOURCE_DIR) && LC_ALL=C git status | grep -q "nothing to commit .working directory clean." + ) + + SET( AUTOBUILD_COMMAND + COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/package/*.tar.bz2 + COMMAND ${CMAKE_MAKE_PROGRAM} package_source + COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 ${CMAKE_BINARY_DIR}/package + COMMAND ${CMAKE_COMMAND} -E remove ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.bz2 +# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/package/${PACKAGE}.changes" "${CMAKE_BINARY_DIR}/package/${PACKAGE}.changes" + ) + + ADD_CUSTOM_TARGET( srcpackage_local + ${AUTOBUILD_COMMAND} + ) + + ADD_CUSTOM_TARGET( srcpackage + COMMAND ${CMAKE_MAKE_PROGRAM} svncheck + ${AUTOBUILD_COMMAND} + ) +ENDMACRO(GENERATE_PACKAGING) diff --git a/cmake/Modules/LibFindMacros.cmake b/cmake/Modules/LibFindMacros.cmake new file mode 100755 index 0000000..69975c5 --- /dev/null +++ b/cmake/Modules/LibFindMacros.cmake @@ -0,0 +1,99 @@ +# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments +# used for the current package. For this to work, the first parameter must be the +# prefix of the current package, then the prefix of the new package etc, which are +# passed to find_package. +macro (libfind_package PREFIX) + set (LIBFIND_PACKAGE_ARGS ${ARGN}) + if (${PREFIX}_FIND_QUIETLY) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) + endif (${PREFIX}_FIND_QUIETLY) + if (${PREFIX}_FIND_REQUIRED) + set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) + endif (${PREFIX}_FIND_REQUIRED) + find_package(${LIBFIND_PACKAGE_ARGS}) +endmacro (libfind_package) + +# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) +# where they added pkg_check_modules. Consequently I need to support both in my scripts +# to avoid those deprecated warnings. Here's a helper that does just that. +# Works identically to pkg_check_modules, except that no checks are needed prior to use. +macro (libfind_pkg_check_modules PREFIX PKGNAME) + if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + include(UsePkgConfig) + pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) + else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif (PKG_CONFIG_FOUND) + endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) +endmacro (libfind_pkg_check_modules) + +# Do the final processing once the paths have been detected. +# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain +# all the variables, each of which contain one include directory. +# Ditto for ${PREFIX}_PROCESS_LIBS and library files. +# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. +# Also handles errors in case library detection was required, etc. +macro (libfind_process PREFIX) + # Skip processing if already processed during this run + if (NOT ${PREFIX}_FOUND) + # Start with the assumption that the library was found + set (${PREFIX}_FOUND TRUE) + + # Process all includes and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_INCLUDES}) + if (${i}) + set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Process all libraries and set _FOUND to false if any are missing + foreach (i ${${PREFIX}_PROCESS_LIBS}) + if (${i}) + set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) + mark_as_advanced(${i}) + else (${i}) + set (${PREFIX}_FOUND FALSE) + endif (${i}) + endforeach (i) + + # Print message and/or exit on fatal error + if (${PREFIX}_FOUND) + if (NOT ${PREFIX}_FIND_QUIETLY) + message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") + endif (NOT ${PREFIX}_FIND_QUIETLY) + else (${PREFIX}_FOUND) + if (${PREFIX}_FIND_REQUIRED) + foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) + message("${i}=${${i}}") + endforeach (i) + message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") + endif (${PREFIX}_FIND_REQUIRED) + endif (${PREFIX}_FOUND) + endif (NOT ${PREFIX}_FOUND) +endmacro (libfind_process) + +macro(libfind_library PREFIX basename) + set(TMP "") + if(MSVC80) + set(TMP -vc80) + endif(MSVC80) + if(MSVC90) + set(TMP -vc90) + endif(MSVC90) + set(${PREFIX}_LIBNAMES ${basename}${TMP}) + if(${ARGC} GREATER 2) + set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) + string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) + set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) + endif(${ARGC} GREATER 2) + find_library(${PREFIX}_LIBRARY + NAMES ${${PREFIX}_LIBNAMES} + PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} + ) +endmacro(libfind_library) + diff --git a/cmake/Modules/SourceDistribution.cmake b/cmake/Modules/SourceDistribution.cmake new file mode 100755 index 0000000..c4dae25 --- /dev/null +++ b/cmake/Modules/SourceDistribution.cmake @@ -0,0 +1,218 @@ +# - a CMake module that helps to create a source distribution
+#
+# This module provide some macros that setup a source distribution.
+# In contrast to standard CPack processing this is a very lightweight
+# module that works very fast. The source distribution module enables
+# the Cmake user to add indivdiual files and directories and thus
+# provides a more fine grained control than CPack.
+#
+# The module works similar to the standard CMake INSTALL command: the
+# macros of this module prepare CMake files (cmake_src_dist.cmake) that
+# contain all necessary commands to create the distribution directoy.
+# The make target 'src_dist' executes the commands and builds the
+# compressed tar file of the source distribution.
+#
+# Usage:
+# src_distribution_init([NOT_INCLUDE_DEFAULT] [<distribtuion name>])
+# Initializes the source distribution functions. Each CMakeList.txt
+# that distributes sources must call this macro before it can use
+# other source distrbution macros.
+# Only the first call from the top level CMakeLists.txt uses the
+# distribution name argument. All subsequent call silently ignore it.
+# The macro sets the distribution name to ${PROJECT_NAME}-{VERSION}
+# if no distribution name is provided.
+# The macro automatically includes some default files and directories
+# into the distribution: CMakeLists.txt and the cmake directory.
+# Set NOT_INCLUDE_DEFAULT to disable this function.
+# The macro creates a make target 'src_dist'. This target executes
+# all operations to create the distribution directory structure and
+# to create the compressed tar file <distrbution name>.tar.gz. The
+# source distribution directory can be deleted afterwards.
+#
+# add_src_dist_dirs(<DIRECTORY> [<DIRECTORY>]*)
+# Works imilar to the normal add_subdirectory command of CMake.
+# This call adds a subdirectory that contains sources or other
+# files that go into a source distribution. The subdirecty must
+# contain a CMakeLists.txt file that also uses the source distrbution
+# macros.
+#
+# add_src_dist_files(<FILENAME> [<FILENAME>]*)
+# Adds one or more files to the source distrbution.
+#
+# Eaxample:
+#
+# include(SourceDistrbution)
+#
+# The following call initializes the module and sets the distrbution's
+# name to 'mySourceDist'. The macro creates a directory with this name
+# in the current build directory and include the standard CMakeLists.txt
+# file and the 'cmake' directory (it it exists) into the distribution.
+#
+# src_distribution_init(mySourceDist)
+#
+# Now add some files (assumes ${src_files} was set previously):
+# add_src_dist_files(README LICENSE ${src_files})
+#
+# Now add a subdirectoy, in this case an include directory:
+# add_src_dist_dirs(include)
+#
+#
+# ---- internal macros ----
+#
+# This macro gets the current directory relative to CMAKE_SOURCE_DIR
+# and sets an internal variable to the current distribution directory.
+# Another variable holds the current path to the CMake command file.
+# Other macros use these variable to construct commands
+# to build the distribution structure.
+#
+MACRO (_set_src_dist_scope_vars)
+STRING(REPLACE "${CMAKE_SOURCE_DIR}" "" _src_dist_subdir "${CMAKE_CURRENT_SOURCE_DIR}")
+if (NOT _src_dist_subdir)
+ set(_src_dist_fulldir ${SRC_DIST_DIR})
+else()
+ set(_src_dist_fulldir ${SRC_DIST_DIR}${_src_dist_subdir})
+endif()
+set(_src_dist_cmd_file_path ${CMAKE_CURRENT_BINARY_DIR}/${_SRC_DIST_CMD_FILE_NAME})
+ENDMACRO()
+
+#
+# Check for the NOT_INCLUDE_DEFAULT option.
+#
+MACRO(_src_dist_parse_options _result _default _length)
+ set(${_default} TRUE)
+
+ foreach(_arg ${ARGN})
+ if (_arg STREQUAL "NOT_INCLUDE_DEFAULT")
+ set(${_default} FALSE)
+ endif()
+ endforeach()
+
+ set(${_result} ${ARGN})
+ list(LENGTH ${_result} ${_length})
+ if (${_length} GREATER 0)
+ list(REMOVE_ITEM ${_result} "NOT_INCLUDE_DEFAULT")
+ endif()
+ # recompute length of list
+ list(LENGTH ${_result} ${_length})
+
+ENDMACRO()
+
+
+#
+# This macro initializes the source distribution package.
+# Only the top-level initialization macro init_src_distribution()
+# calls this internal macro.
+#
+MACRO (_src_dist_internal_init)
+# internal variable for distribution cmake file
+set(_SRC_DIST_CMD_FILE_NAME "cmake_src_dist.cmake")
+
+if (${_src_dist_dirlist_length} EQUAL 0)
+ set(_src_dist_tardir ${PROJECT_NAME}-${VERSION})
+else()
+ list(GET _src_dist_dirlist 0 _src_dist_tardir)
+endif()
+set(SRC_DIST_DIR ${CMAKE_BINARY_DIR}/${_src_dist_tardir})
+
+message(STATUS "Source distribution direcrory set to: ${SRC_DIST_DIR}")
+
+_set_src_dist_scope_vars()
+file(REMOVE ${_src_dist_cmd_file_path})
+
+# fill in first commands into the distribution cmake file. Calling
+# 'make src_dist' executes the stored commands and prepares the source
+# distrubtion.
+#
+file(APPEND ${_src_dist_cmd_file_path} "
+# clear contents of an existing distribution directory
+file(REMOVE_RECURSE ${SRC_DIST_DIR})
+")
+
+add_custom_target(src_dist
+ COMMAND ${CMAKE_COMMAND} -P ${_src_dist_cmd_file_path}
+ COMMAND ${CMAKE_COMMAND} -E tar cfj ${SRC_DIST_DIR}.tar.bz2 ${_src_dist_tardir}
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${SRC_DIST_DIR}
+ )
+
+ENDMACRO()
+
+################# User visible macros ###################
+#
+MACRO(src_distribution_init)
+
+# clear old src distribution cmake command file
+_src_dist_parse_options(_src_dist_dirlist _src_dist_default _src_dist_dirlist_length ${ARGN})
+
+if (NOT DEFINED _SRC_DIST_INIT)
+ _src_dist_internal_init()
+ set(_SRC_DIST_INIT TRUE)
+else()
+ _set_src_dist_scope_vars()
+ file(REMOVE ${_src_dist_cmd_file_path})
+endif()
+
+if(_src_dist_default)
+ if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+ set(_src_dist_list_tmp)
+ # Get all files names in cmake subdir
+ # Unfortunately CMake also globs all directories and files that start
+ # with . - that is not the same as shell behaviour
+ file(GLOB_RECURSE _src_dist_names_tmp RELATIVE
+ ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/*)
+ #
+ # Remove all file names that contain a name that start with .
+ foreach(_nm ${_src_dist_names_tmp})
+ string(REGEX REPLACE .*/\\..* "" _nm ${_nm})
+ set(_src_dist_list_tmp ${_src_dist_list_tmp} ${_nm})
+ endforeach()
+ add_src_dist_files(${_src_dist_list_tmp})
+ endif()
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt")
+ file(APPEND ${_src_dist_cmd_file_path} "
+FILE(INSTALL DESTINATION \"${_src_dist_fulldir}\" TYPE FILE FILES
+\"${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt\")
+")
+ endif()
+endif()
+
+ENDMACRO()
+
+# Add a subdirectory to the src distribution
+#
+MACRO(add_src_dist_dirs)
+
+foreach(_dir ${ARGN})
+ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_dir}/CMakeLists.txt)
+ message(FATAL_ERROR
+ "Soure distribution subdirectory \"${CMAKE_CURRENT_SOURCE_DIR}/${_dir}\" does not contain a CMakeLists.txt")
+ endif()
+
+ # include subdirectory's distribution cmake command file
+ file(APPEND ${_src_dist_cmd_file_path} "
+include(\"${CMAKE_CURRENT_BINARY_DIR}/${_dir}/${_SRC_DIST_CMD_FILE_NAME}\")
+")
+endforeach()
+ENDMACRO()
+
+#
+# Add files to the src distribution. The handles and install files
+# that are in the same directory as the current source as well as files
+# in sub directories of the current source (with relative path).
+#
+MACRO(add_src_dist_files)
+
+foreach(_file ${ARGN})
+ get_filename_component(_src_dist_tmp_path ${_file} PATH)
+# string(REPLACE "${CMAKE_SOURCE_DIR}" "" _src_dist_tmp_path "${_src_dist_tmp_path}")
+ if(_src_dist_tmp_path)
+ set(_src_dist_tmp_path ${_src_dist_fulldir}/${_src_dist_tmp_path})
+ else ()
+ set(_src_dist_tmp_path ${_src_dist_fulldir})
+ endif()
+ file(APPEND ${_src_dist_cmd_file_path} "
+FILE(INSTALL DESTINATION \"${_src_dist_tmp_path}\" TYPE FILE FILES
+ \"${CMAKE_CURRENT_SOURCE_DIR}/${_file}\") ")
+
+endforeach()
+
+ENDMACRO()
diff --git a/cmake/Modules/UseRPMTools.cmake b/cmake/Modules/UseRPMTools.cmake new file mode 100644 index 0000000..a612208 --- /dev/null +++ b/cmake/Modules/UseRPMTools.cmake @@ -0,0 +1,177 @@ +# +# - Find tools needed for building RPM Packages +# on Linux systems and defines macro that helps to +# build source or binary RPM, the MACRO assumes +# CMake 2.4.x which includes CPack support. +# CPack is used to build tar.gz source tarball +# which may be used by a custom user-made spec file. +# +# - Define RPMTools_ADD_RPM_TARGETS which defines +# two (top-level) CUSTOM targets for building +# source and binary RPMs +# +# Those CMake macros are provided by the TSP Developer Team +# https://savannah.nongnu.org/projects/tsp +# +# Modified by Werner to use the SoureDistribution variables and +# files instead of CPack stuff. Only minor changes. + +IF (WIN32) + MESSAGE(STATUS "RPM tools not available on Win32 systems") +ENDIF(WIN32) + +IF (UNIX) + # Look for RPM builder executable + FIND_PROGRAM(RPMTools_RPMBUILD_EXECUTABLE + NAMES rpmbuild + PATHS "/usr/bin;/usr/lib/rpm" + PATH_SUFFIXES bin + DOC "The RPM builder tool") + + IF (RPMTools_RPMBUILD_EXECUTABLE) + MESSAGE(STATUS "Looking for RPMTools... - found rpmuild is ${RPMTools_RPMBUILD_EXECUTABLE}") + SET(RPMTools_RPMBUILD_FOUND "YES") + GET_FILENAME_COMPONENT(RPMTools_BINARY_DIRS ${RPMTools_RPMBUILD_EXECUTABLE} PATH) + ELSE (RPMTools_RPMBUILD_EXECUTABLE) + SET(RPMTools_RPMBUILD_FOUND "NO") + MESSAGE(STATUS "Looking for RPMTools... - rpmbuild NOT FOUND") + ENDIF (RPMTools_RPMBUILD_EXECUTABLE) + + # Detect if CourceDistribution was initialized or not + IF (NOT DEFINED "SRC_DIST_DIR") + MESSAGE(FATAL_ERROR "SourceDistribution was not initialized") + ENDIF (NOT DEFINED "SRC_DIST_DIR") + + IF (RPMTools_RPMBUILD_FOUND) + SET(RPMTools_FOUND TRUE) + # + # - first arg (ARGV0) is RPM name + # - second arg (ARGV1) is the RPM spec file path [optional] + # - third arg (ARGV2) is the RPM ROOT DIRECTORY used to build RPMs [optional] + # + MACRO(RPMTools_ADD_RPM_TARGETS RPMNAME) + + # + # If no spec file is provided create a minimal one + # + IF ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${CMAKE_BINARY_DIR}/${RPMNAME}.spec") + ELSE ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${ARGV1}") + ENDIF("${ARGV1}" STREQUAL "") + + # Verify whether if RPM_ROOTDIR was provided or not + IF("${ARGV2}" STREQUAL "") + SET(RPM_ROOTDIR ${CMAKE_BINARY_DIR}/RPM) + ELSE ("${ARGV2}" STREQUAL "") + SET(RPM_ROOTDIR "${ARGV2}") + ENDIF("${ARGV2}" STREQUAL "") + MESSAGE(STATUS "RPMTools:: Using RPM_ROOTDIR=${RPM_ROOTDIR}") + + # Prepare RPM build tree + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/tmp) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/BUILD) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/RPMS) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SOURCES) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SPECS) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SRPMS) + + # + # We check whether if the provided spec file is + # to be configure or not. + # + IF ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec") + SET(SPECFILE_NAME "${RPMNAME}.spec") + MESSAGE(STATUS "No Spec file given generate a minimal one --> ${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec") + FILE(WRITE ${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec + "# -*- rpm-spec -*- +Summary: ${RPMNAME} +Name: ${RPMNAME} +Version: ${PACKAGE_VERSION} +Release: 1 +License: Unknown +Group: Unknown +Source: ${SRC_DIST_DIR}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildRequires: cmake + +%define prefix /opt/${RPMNAME}-%{version} +%define rpmprefix $RPM_BUILD_ROOT%{prefix} +%define srcdirname %{name}-%{version}-Source + +%description +${RPMNAME} : No description for now + +%prep +%setup -q -n %{srcdirname} + +%build +cd .. +rm -rf build_tree +mkdir build_tree +cd build_tree +cmake -DCMAKE_INSTALL_PREFIX=%{rpmprefix} ../%{srcdirname} +make + +%install +cd ../build_tree +make install + +%clean +rm -rf %{srcdirname} +rm -rf build_tree + +%files +%defattr(-,root,root,-) +%dir %{prefix} +%{prefix}/* + +%changelog +* Wed Feb 28 2007 Erk <eric.noulard@gmail.com> + Generated by CMake UseRPMTools macros" + ) + + ELSE ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${ARGV1}") + + GET_FILENAME_COMPONENT(SPECFILE_EXT ${SPECFILE_PATH} EXT) + IF ("${SPECFILE_EXT}" STREQUAL ".spec") + # This is a 'ready-to-use' spec file which does not need to be CONFIGURED + GET_FILENAME_COMPONENT(SPECFILE_NAME ${SPECFILE_PATH} NAME) + MESSAGE(STATUS "Simple copy spec file <${SPECFILE_PATH}> --> <${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME}>") + CONFIGURE_FILE( + ${SPECFILE_PATH} + ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + COPYONLY) + ELSE ("${SPECFILE_EXT}" STREQUAL ".spec") + # This is a to-be-configured spec file + GET_FILENAME_COMPONENT(SPECFILE_NAME ${SPECFILE_PATH} NAME_WE) + SET(SPECFILE_NAME "${SPECFILE_NAME}.spec") + MESSAGE(STATUS "Configuring spec file <${SPECFILE_PATH}> --> <${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME}>") + CONFIGURE_FILE( + ${SPECFILE_PATH} + ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + @ONLY) + ENDIF ("${SPECFILE_EXT}" STREQUAL ".spec") + ENDIF("${ARGV1}" STREQUAL "") + + ADD_CUSTOM_TARGET(${RPMNAME}_srpm + COMMAND ${CMAKE_BUILD_TOOL} src_dist + COMMAND ${CMAKE_COMMAND} -E copy ${SRC_DIST_DIR}.tar.gz ${RPM_ROOTDIR}/SOURCES + COMMAND ${RPMTools_RPMBUILD_EXECUTABLE} -bs --define=\"_topdir ${RPM_ROOTDIR}\" --buildroot=${RPM_ROOTDIR}/tmp ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + ) + + ADD_CUSTOM_TARGET(${RPMNAME}_rpm + COMMAND ${CMAKE_BUILD_TOOL} src_dist + COMMAND ${CMAKE_COMMAND} -E copy ${SRC_DIST_DIR}.tar.gz ${RPM_ROOTDIR}/SOURCES + COMMAND ${RPMTools_RPMBUILD_EXECUTABLE} -bb --define=\"_topdir ${RPM_ROOTDIR}\" --buildroot=${RPM_ROOTDIR}/tmp ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + ) + ENDMACRO(RPMTools_ADD_RPM_TARGETS) + + ELSE (RPMTools_RPMBUILD_FOUND) + SET(RPMTools FALSE) + ENDIF (RPMTools_RPMBUILD_FOUND) + +ENDIF (UNIX) diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..691ab38 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,20 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling ${file}") + IF(EXISTS "${file}") + EXEC_PROGRAM(@CMAKE_COMMAND@ -E remove "${file}" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing ${file}") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File ${file} does not exist.") + ENDIF(EXISTS "${file}") +ENDFOREACH(file) diff --git a/debian/backports/sarge b/debian/backports/sarge new file mode 100755 index 0000000..c71a971 --- /dev/null +++ b/debian/backports/sarge @@ -0,0 +1,13 @@ +#!/bin/bash +# +# Hook for automatic backports at buildserver.net +# +# Target dist: Debian Sarge + +# Downgrade to debhelper 4 compat +sed -i -e 's#^\(.*DH_COMPAT\)=.*$#\1=4#' debian/rules +sed -i -e 's#^\(Build-Depends:.*\)debhelper[^,$]*[\ ,$]\+\(.*\)$#\1debhelper (>= 4.2.32), \2#' debian/control + +echo 4 >debian/compat + +exit 0 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7ec7a34 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,237 @@ +libzrtpcpp (2.3.4-1) unstable; urgency=medium + + * New upstream release + - Fixes "CVE-2013-2221 CVE-2013-2222 CVE-2013-2223" (Closes: #714650) + + -- Mark Purcell <msp@debian.org> Wed, 10 Jul 2013 00:55:55 +1000 + +libzrtpcpp (2.3.2-2) unstable; urgency=low + + * Upload to unstable + * Update debian/watch - Thanks Bart + * Update debian/control - Vcs & Standards-Version: + + -- Mark Purcell <msp@debian.org> Sun, 12 May 2013 19:08:46 +1000 + +libzrtpcpp (2.3.2-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell <msp@debian.org> Wed, 26 Dec 2012 11:30:07 +1100 + +libzrtpcpp (2.1.2-1) experimental; urgency=low + + * New upstream release + - Fixes "new upstream version available" (Closes: #678975) + + -- Mark Purcell <msp@debian.org> Tue, 26 Jun 2012 07:57:13 +1000 + +libzrtpcpp (2.0.0-3) unstable; urgency=low + + * wheezy polish - lintian clean + * Update Standards-Version: 3.9.3 + * Add source/format 3.0 (quilt) + * Update debian/copyright - fix copyright-refers-to-symlink-license + * debian/compat -> 9 + + -- Mark Purcell <msp@debian.org> Sun, 24 Jun 2012 22:09:11 +1000 + +libzrtpcpp (2.0.0-2) unstable; urgency=low + + * Added Build-Depends: cmake + * Upstream: + - Fixes "FTBFS with gcc-4.7: missing include" (Closes: #672942) + - Fixes "FTBFS in sid: No package 'libccrtp1' found" (Closes: #673255) + + -- Mark Purcell <msp@debian.org> Sat, 19 May 2012 21:39:32 +1000 + +libzrtpcpp (2.0.0-1) unstable; urgency=low + + * New upstream release + * NEW package libzrtpcpp2 - match-soname + * Upload to unstable - coordinated through debian-release + * Switch to dh 7 + * Add Build-Depends: libucommon & libssl + * Ack NMUs - Thanks Matthias & Andreas + + -- Mark Purcell <msp@debian.org> Sat, 19 May 2012 11:16:39 +1000 + +libzrtpcpp (1.4.6-1.2) unstable; urgency=low + + * Non maintainer upload. + * Fix build failure with GCC 4.7. Closes: #667261. + + -- Matthias Klose <doko@debian.org> Tue, 17 Apr 2012 08:32:37 +0200 + +libzrtpcpp (1.4.6-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Stop shipping libtool la file. Closes: #620607 + http://wiki.debian.org/ReleaseGoals/LAFileRemoval + + -- Andreas Metzler <ametzler@debian.org> Sun, 24 Apr 2011 14:13:20 +0200 + +libzrtpcpp (1.6.0-1) experimental; urgency=low + + * New upstream release + * NEW package libzrtpcpp-1.6-0 - soname bump + + -- Mark Purcell <msp@debian.org> Sun, 30 Jan 2011 18:46:47 +1100 + +libzrtpcpp (1.4.6-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell <msp@debian.org> Fri, 30 Oct 2009 23:00:05 +1100 + +libzrtpcpp (1.4.5-1) unstable; urgency=low + + * New upstream release + + [ Kilian Krause ] + * Remove -N from wget args in get-orig-source target as -O is already + used. + + -- Mark Purcell <msp@debian.org> Wed, 15 Jul 2009 20:36:59 +1000 + +libzrtpcpp (1.4.3-3) unstable; urgency=low + + * Upload to unstable + - libcommoncpp2 transition + + -- Mark Purcell <msp@debian.org> Thu, 26 Feb 2009 18:31:25 +1100 + +libzrtpcpp (1.4.3-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell <msp@debian.org> Sun, 08 Feb 2009 11:26:39 +1100 + +libzrtpcpp (1.4.2-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell <msp@debian.org> Tue, 03 Feb 2009 21:29:49 +1100 + +libzrtpcpp (1.4.1-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell <msp@debian.org> Tue, 23 Dec 2008 17:36:27 +1100 + +libzrtpcpp (1.4.0-1) experimental; urgency=low + + * New upstream release + - first release that is conformant to the ZRTP specification + + [ Patrick Matthäi ] + * Bumped to Standards-Version 3.8.0. + + [ Mark Purcell ] + * NEW libzrtpcpp-1.4-0 - lintian:package-name-doesnt-match-sonames + * Add ${misc:Depends} - fixes lintian:debhelper-but-no-misc-depends + * debian/watch opts=pasv + * Update debian/copyright + + -- Mark Purcell <msp@debian.org> Sun, 30 Nov 2008 11:42:37 +1100 + +libzrtpcpp (1.3.0-1) unstable; urgency=low + + * New upstream release + * New Package: libzrtpcpp-1.3-0 - soname bump + - Upload discussed with debian-release + + -- Mark Purcell <msp@debian.org> Sun, 22 Jun 2008 10:01:03 +1000 + +libzrtpcpp (1.2.0-1) unstable; urgency=low + + * New upstream release + - Implements latest changes to ZRTP draft + * DEB_INSTALL_CHANGELOGS_ALL := NEWS + * New Package: libzrtpcpp-1.2-0 soname bump + - Upload cleared with debian-release + + -- Mark Purcell <msp@debian.org> Fri, 13 Jun 2008 08:18:39 +1000 + +libzrtpcpp (1.1.0-1) unstable; urgency=low + + * New upstream release + * Package: libzrtpcpp-1.1-0 due to soname bump + + -- Mark Purcell <msp@debian.org> Tue, 29 Apr 2008 22:52:58 +1000 + +libzrtpcpp (0.9.2-3) unstable; urgency=low + + [ Mark Purcell ] + * Bump LT_RELEASE="0.9.2deb" to cater for upstream ABI breakage + - ABI breakage - sendDataRTP removed in 0.9.2 (Closes: #445657) + * Cleanup get-orig-source target + + -- Mark Purcell <msp@debian.org> Sat, 20 Oct 2007 12:03:24 +0100 + +libzrtpcpp (0.9.2-2) unstable; urgency=high + + * Fix package names to reflect new ABI - leaving the internal SOVER + unchanged (Closes: #445657) + * High urgency due to fixing RC bug. + * Add myself to Uploaders to make this a non-NMU + * Adjust DEB_DH_MAKESHLIBS_ARGS_libzrtpcpp-0.9-1 := -V"libzrtpcpp-0.9-1" + + -- Kilian Krause <kilian@debian.org> Mon, 15 Oct 2007 21:42:05 +0200 + +libzrtpcpp (0.9.2-1) unstable; urgency=low + + * New upstream release + + [ Kilian Krause ] + * Add dpkg-dev (>= 1.13.19) to Build-Depends for binary:Version + + -- Mark Purcell <msp@debian.org> Sun, 30 Sep 2007 12:17:10 +0100 + +libzrtpcpp (0.9.0-6) unstable; urgency=low + + * DEB_DH_MAKESHLIBS_ARGS_libzrtpcpp-0.9-0 := -V"libzrtpcpp-0.9-0 (>= 0.9.0-4)" + - Closes: #434590: twinkle: Crashes on every call when using etch + libraries - Debian Bug report logs + * Use ${binary:Version} libzrtpcpp source: substvar-source-version-is- + deprecated + + -- Mark Purcell <msp@debian.org> Wed, 25 Jul 2007 21:57:34 +0100 + +libzrtpcpp (0.9.0-5) unstable; urgency=low + + * DEB_DH_MAKESHLIBS_ARGS_libzrtpcpp-0.9-0 := -V"libzrtpcpp-0.9-0 (>= 0.9-4)" + * Depends: pkg-config + + -- Mark Purcell <msp@debian.org> Sun, 20 May 2007 16:43:12 +0100 + +libzrtpcpp (0.9.0-4) unstable; urgency=low + + * Rebuild for libccrtp-dev > 1.5.1 [libccrtp soname] + + -- Mark Purcell <msp@debian.org> Thu, 12 Apr 2007 21:36:27 +0100 + +libzrtpcpp (0.9.0-3) unstable; urgency=low + + * Rebuild for libccrtp-dev (>= 1.5.0-3) [libcommoncpp2 soname] + + -- Mark Purcell <msp@debian.org> Sun, 7 Jan 2007 06:11:31 +0000 + +libzrtpcpp (0.9.0-2) unstable; urgency=low + + [ Mikael Magnusson ] + * Add Build-Depends: pkg-config + * Add Uploaders: Mikael Magnusson <mikma@users.sourceforge.net> + + [ Mark Purcell ] + * Maintainer: Debian VoIP Team <pkg-voip-maintainers@lists.alioth.debian.org> + * Initial release (Closes: #390591) + + -- Mark Purcell <msp@debian.org> Thu, 12 Oct 2006 23:12:26 +1000 + +libzrtpcpp (0.9.0-1) unstable; urgency=low + + * Initial release (Closes: #390591) + + -- Mark Purcell <msp@debian.org> Mon, 2 Oct 2006 08:51:56 +0800 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..4c12d9c --- /dev/null +++ b/debian/control @@ -0,0 +1,33 @@ +Source: libzrtpcpp +Priority: optional +Maintainer: Debian VoIP Team <pkg-voip-maintainers@lists.alioth.debian.org> +Uploaders: Mark Purcell <msp@debian.org>, Mikael Magnusson <mikma@users.sourceforge.net>, Kilian Krause <kilian@debian.org> +Build-Depends: debhelper (>= 9), autotools-dev, pkg-config, dpkg-dev (>= 1.13.19), cmake, + libucommon-dev, libccrtp-dev (>= 2.0.0), libssl-dev, libgcrypt11-dev | libgcrypt-dev +Standards-Version: 3.9.4 +Section: libs +Homepage: http://www.gnu.org/software/ccrtp/ +Vcs-Svn: svn://anonscm.debian.org/pkg-voip/libzrtpcpp/trunk/ +Vcs-Browser: http://anonscm.debian.org/viewvc/pkg-voip/libzrtpcpp/trunk/ + +Package: libzrtpcpp-dev +Section: libdevel +Architecture: any +Depends: ${misc:Depends}, libzrtpcpp2 (= ${binary:Version}), pkg-config +Description: Headers and static link library for libzrtpcpp + This library is an extension to the GNU RTP Stack, libccrtp, + that offers compatibility with Phil Zimmermann's zrtp/Zfone voice + encryption, and which can be directly embedded into telephony + applications. The current release is conformant to the ZRTP specification. + . + This package provides the header files, link libraries, and + documentation for building applications that use libzrtpcpp. + +Package: libzrtpcpp2 +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: ccrtp extension for zrtp/Zfone support + This library is an extension to the GNU RTP Stack, libccrtp, + that offers compatibility with Phil Zimmermann's zrtp/Zfone voice + encryption, and which can be directly embedded into telephony + applications. The current release is conformant to the ZRTP specification. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..889bdb6 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,146 @@ +This package was debianized by Mark Purcell <msp@debian.org> on +Mon, 2 Oct 2006 08:51:56 +0800. + +It was downloaded from ftp://ftp.gnu.org/gnu/ccrtp/libzrtpcpp-0.9.0.tar.gz + +Upstream Author: Werner Dittmann <Werner.Dittmann@t-online.de> + + [Copyright: 2006-2011 Werner Dittmann] + +License: + +Please note, this library is licensed under the GNU GPL, version 3 or +later, and has been copyright assigned to the Free Software Foundation. + + 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 <http://www.gnu.org/licenses/>. + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL-3'. + +Other portions: + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + +On Debian systems, the complete text of the GNU Lesser General +Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. + +The Debian packaging is (Copyright) 2006, Mark Purcell <msp@debian.org> and +is licensed under the GPL-3, see above. + +src/Base32.cxx: UNKNOWN + [Copyright: 2002 Bryce "Zooko" Wilcox-O'Hearn Permission is hereby / HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER] + * Copyright (c) 2002 Bryce "Zooko" Wilcox-O'Hearn Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software to + * deal in this software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of this software, and to permit persons to whom this software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of this software. + * + * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THIS SOFTWARE. + * + * Converted to C++ by: + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + +src/libzrtpcpp/crypto/openssl/hmac256.cxx: LGPL (v2.1 or later) (with incorrect FSF address) + [Copyright: holders give / 2005, 2004 Erik Eliasson, Johan Bilien] +src/libzrtpcpp/crypto/openssl/sha256.cxx: LGPL (v2.1 or later) (with incorrect FSF address) + [Copyright: holders give / 2005, 2004 Erik Eliasson, Johan Bilien] + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + +src/ZrtpCrc32.cxx: GPL (with incorrect FSF address) + [Copyright: 1999-2001 Motorola, Inc / 2001, D. Otis. Use this program, code or tables */ / 2001-2003 International Business Machines, Corp] +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * + * SCTP Checksum functions + * + * The SCTP reference implementation 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, or (at your option) + * any later version. + * + * The SCTP reference implementation 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. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers <lksctp-developers@lists.sourceforge.net> + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Dinakaran Joseph + * Jon Grimm <jgrimm@us.ibm.com> + * Sridhar Samudrala <sri@us.ibm.com> + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +/* The following code has been taken directly from + * draft-ietf-tsvwg-sctpcsum-03.txt + * + * The code has now been modified by Werner.Dittmann@t-online.de for use + * inside the ZRTP implementation. + */ + +src/libzrtpcpp/crypto/twofish.* + [Copyright: 2002 by Niels Ferguson] + + * The author hereby grants a perpetual license to everybody to + * use this code for any purpose as long as the copyright message is included + * in the source code of this or any derived work. + * + * Yes, this means that you, your company, your club, and anyone else + * can use this code anywhere you want. You can change it and distribute it + * under the GPL, include it in your commercial product without releasing + * the source code, put it on the web, etc. + * The only thing you cannot do is remove my copyright message, + * or distribute any source code based on this implementation that does not + * include my copyright message. + diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README.md diff --git a/debian/libzrtpcpp-dev.install b/debian/libzrtpcpp-dev.install new file mode 100644 index 0000000..5774e64 --- /dev/null +++ b/debian/libzrtpcpp-dev.install @@ -0,0 +1,3 @@ +usr/include/* +usr/lib/lib*.so +usr/lib/pkgconfig/* diff --git a/debian/libzrtpcpp2.install b/debian/libzrtpcpp2.install new file mode 100644 index 0000000..d0dbfd1 --- /dev/null +++ b/debian/libzrtpcpp2.install @@ -0,0 +1 @@ +usr/lib/lib*.so.* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b0e0606 --- /dev/null +++ b/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f + +export USES_CCRTP_INCLUDE_DIRS=yes + +%: + dh $@ --parallel --with autotools-dev diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..706ebec --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=3 +http://ftp.gnu.org/gnu/ccrtp/libzrtpcpp(?:[_\-]?|[_\-]v)(\d\S*)\.(?:tar\.xz|txz|tar\.bz2|tbz2|tar\.gz|tgz|zip)(?:[^\.]\S*)? debian svn-upgrade +# Bart Martens <bartm@debian.org> Sat, 02 Mar 2013 16:58:05 +0000 diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt new file mode 100755 index 0000000..015807c --- /dev/null +++ b/demo/CMakeLists.txt @@ -0,0 +1,19 @@ +########### next target ############### + +add_executable(zrtptest zrtptest.cpp) +target_link_libraries(zrtptest zrtpcpp ccrtp commoncpp) +add_dependencies(zrtptest zrtpcpp) + +########### next target ############### + +add_executable(zrtptestMulti zrtptestMulti.cpp) +target_link_libraries(zrtptestMulti zrtpcpp ccrtp commoncpp) +add_dependencies(zrtptestMulti zrtpcpp) + +########### next target ############### + +#add_executable(wrappertest wrappertest.c) +#target_link_libraries(wrappertest zrtpcpp) + +########### install files ############### +# None diff --git a/demo/README b/demo/README new file mode 100644 index 0000000..42ecf8b --- /dev/null +++ b/demo/README @@ -0,0 +1,27 @@ + +This directory includes example programs intended for testing +and illustrating features of ccRTP and the ZRTP extension. + +Before using these programs make sure that ccRTP is up and +working correctly. + +* zrtptest: is similar to ccrtptest in ccRTP demo directory. This + program shows how to use the class SymmetricZRTPSession instead + of RTPSession. + + The first test shows that SymmetricZRTPSession is compatible to + RTPSession if it is used without specific initialization or configuration. + + The second test initializes the ZRTP engine and starts it just before + sending or receiving RTP data. The enable ZRTP the test sets RTP in + bi-directional mode. This is the main difference to the first test case. + + The third test shows how to use an application supplied callback class + to control message printout, switching to secure mode, displaying + the Short Authentication String (SAS). + +To start the demo application you may open two shell (command) windows +and start "zrtptest -r" in one window first, then start "zrtptest -s" +in the second window. The application use the port numbers 10002 thruogh +10004 on localhost to communicate. + diff --git a/demo/wrappertest.c b/demo/wrappertest.c new file mode 100644 index 0000000..abbb3cf --- /dev/null +++ b/demo/wrappertest.c @@ -0,0 +1,169 @@ +/* + This class maps the ZRTP C calls to ZRTP C++ methods. + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#include <libzrtpcpp/ZrtpCWrapper.h> + +#include <stdio.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Forward declaration of thethe ZRTP specific callback functions that this + adapter must implement */ +static int32_t zrtp_sendDataZRTP (ZrtpContext* ctx, const uint8_t* data, int32_t length ) ; +static int32_t zrtp_activateTimer (ZrtpContext* ctx, int32_t time ) ; +static int32_t zrtp_cancelTimer(ZrtpContext* ctx) ; +static void zrtp_sendInfo (ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; +static int32_t zrtp_srtpSecretsReady (ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part ) ; +static void zrtp_srtpSecretsOff (ZrtpContext* ctx, int32_t part ) ; +static void zrtp_rtpSecretsOn (ZrtpContext* ctx, char* c, char* s, int32_t verified ) ; +static void zrtp_handleGoClear(ZrtpContext* ctx) ; +static void zrtp_zrtpNegotiationFailed(ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; +static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx) ; +static void zrtp_synchEnter(ZrtpContext* ctx) ; +static void zrtp_synchLeave(ZrtpContext* ctx) ; +static void zrtp_zrtpAskEnrollment (ZrtpContext* ctx, char* info ) ; +static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, char* info ) ; +static void zrtp_signSAS(ZrtpContext* ctx, char* sas) ; +static int32_t zrtp_checkSASSignature (ZrtpContext* ctx, char* sas ) ; + +/* The callback function structure for ZRTP */ +static zrtp_Callbacks c_callbacks = { + &zrtp_sendDataZRTP, + &zrtp_activateTimer, + &zrtp_cancelTimer, + &zrtp_sendInfo, + &zrtp_srtpSecretsReady, + &zrtp_srtpSecretsOff, + &zrtp_rtpSecretsOn, + &zrtp_handleGoClear, + &zrtp_zrtpNegotiationFailed, + &zrtp_zrtpNotSuppOther, + &zrtp_synchEnter, + &zrtp_synchLeave, + &zrtp_zrtpAskEnrollment, + &zrtp_zrtpInformEnrollment, + &zrtp_signSAS, + &zrtp_checkSASSignature +}; + +/* + * Here start with callback functions that support the ZRTP core + */ +static int32_t zrtp_sendDataZRTP (ZrtpContext* ctx, const uint8_t* data, int32_t length ) +{ + return 0; +} + +static int32_t zrtp_activateTimer (ZrtpContext* ctx, int32_t time) +{ + return 0; +} + +static int32_t zrtp_cancelTimer(ZrtpContext* ctx) +{ + return 0; +} + +static void zrtp_sendInfo (ZrtpContext* ctx, int32_t severity, int32_t subCode ) +{ +} + +static int32_t zrtp_srtpSecretsReady (ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part ) +{ + return 0; +} + +static void zrtp_srtpSecretsOff (ZrtpContext* ctx, int32_t part ) +{ +} + +static void zrtp_rtpSecretsOn (ZrtpContext* ctx, char* c, char* s, int32_t verified ) +{ +} + +static void zrtp_handleGoClear(ZrtpContext* ctx) +{ +} + +static void zrtp_zrtpNegotiationFailed (ZrtpContext* ctx, int32_t severity, int32_t subCode ) +{ +} + +static void zrtp_zrtpNotSuppOther(ZrtpContext* ctx) +{ +} + +static void zrtp_synchEnter(ZrtpContext* ctx) +{ +} + +static void zrtp_synchLeave(ZrtpContext* ctx) +{ +} + +static void zrtp_zrtpAskEnrollment(ZrtpContext* ctx, char* info ) +{ + +} +static void zrtp_zrtpInformEnrollment(ZrtpContext* ctx, char* info ) +{ +} + +static void zrtp_signSAS(ZrtpContext* ctx, char* sas) +{ +} + +static int32_t zrtp_checkSASSignature(ZrtpContext* ctx, char* sas ) +{ + return 0; +} + +int main(int argc, char *argv[]) +{ + ZrtpContext* zrtpCtx; + char* hh; + char** names; + + zrtpCtx = zrtp_CreateWrapper (); + zrtp_initializeZrtpEngine(zrtpCtx, &c_callbacks, "test", "test.zid", NULL); + + hh = zrtp_getHelloHash(zrtpCtx); + if (hh != 0) + { + printf("hh: %s\n", hh); + } + else + printf("no hh"); + + zrtp_InitializeConfig(zrtpCtx); + names = zrtp_getAlgorithmNames(zrtpCtx, zrtp_HashAlgorithm); + + for (; *names; names++) { + printf("name: %s\n", *names); + } + + return 0; +} +#ifdef __cplusplus +} +#endif diff --git a/demo/zrtptest.cpp b/demo/zrtptest.cpp new file mode 100644 index 0000000..394d31c --- /dev/null +++ b/demo/zrtptest.cpp @@ -0,0 +1,665 @@ +// Test ZRTP extension for ccRTP +// +// Copyright (C) 2008 Werner Dittmann <Werner.Dittmann@t-online.de> +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include <cstdlib> +#include <map> +#include <libzrtpcpp/zrtpccrtp.h> +#include <libzrtpcpp/ZrtpUserCallback.h> + +using namespace ost; +using namespace std; +using namespace GnuZrtpCodes; + +class PacketsPattern +{ +public: + inline const InetHostAddress& + getDestinationAddress() const + { return destinationAddress; } + + inline const tpport_t + getDestinationPort() const + { return destinationPort; } + + uint32 + getPacketsNumber() const + { return packetsNumber; } + + uint32 + getSsrc() const + { return 0xdeadbeef; } + + const unsigned char* + getPacketData(uint32 i) + { return data[i%2]; } + + const size_t + getPacketSize(uint32 i) + { return strlen((char*)data[i%2]) + 1 ; } + +private: + static const InetHostAddress destinationAddress; + static const uint16 destinationPort = 5002; + static const uint32 packetsNumber = 10; + static const uint32 packetsSize = 12; + static const unsigned char* data[]; +}; + +const InetHostAddress PacketsPattern::destinationAddress = + InetHostAddress("localhost"); + +const unsigned char* PacketsPattern::data[] = { + (unsigned char*)"0123456789\n", + (unsigned char*)"987654321\n" +}; + +PacketsPattern pattern; + +class ExtZrtpSession : public SymmetricZRTPSession { +// ExtZrtpSession(InetMcastAddress& ima, tpport_t port) : +// RTPSession(ima,port) {} +// +// ExtZrtpSession(InetHostAddress& ia, tpport_t port) : +// RTPSession(ia,port) {} + +public: + ExtZrtpSession(uint32 ssrc, const InetHostAddress& ia) : + SingleThreadRTPSession(ssrc, ia){ + cout << "Extended" << endl; + } + + ExtZrtpSession(uint32 ssrc, const InetHostAddress& ia, tpport_t dataPort) : + SingleThreadRTPSession(ssrc, ia, dataPort) { + cout << "Extended" << endl; + } + + ExtZrtpSession(const InetHostAddress& ia, tpport_t dataPort) : + SingleThreadRTPSession(ia, dataPort) { + cout << "Extended" << endl; + } + + void onGotGoodbye(const SyncSource& source, const std::string& reason) + { + cout << "I got a Goodbye packet from " + << hex << (int)source.getID() << "@" + << dec + << source.getNetworkAddress() << ":" + << source.getControlTransportPort() << endl; + cout << " Goodbye reason: \"" << reason << "\"" << endl; + } + // redefined from QueueRTCPManager + void onGotRR(SyncSource& source, RecvReport& RR, uint8 blocks) + { + SingleThreadRTPSession::onGotRR(source,RR,blocks); + cout << "I got an RR RTCP report from " + << hex << (int)source.getID() << "@" + << dec + << source.getNetworkAddress() << ":" + << source.getControlTransportPort() << endl; + } +}; + + +/** + * SymmetricZRTPSession in non-security mode (RTPSession compatible). + * + * The next two classes show how to use <code>SymmetricZRTPSession</code> + * in the same way as <code>RTPSession</code>. This is straightforward, + * just don't do any configuration or initialization. + */ +class SendPacketTransmissionTest: public Thread, public TimerPort { +public: + void + run() { + doTest(); + } + + int doTest() { + // should be valid? + //RTPSession tx(); + ExtZrtpSession tx(pattern.getSsrc(), InetHostAddress("localhost")); +// SymmetricZRTPSession tx(pattern.getSsrc(), InetHostAddress("localhost")); + tx.setSchedulingTimeout(10000); + tx.setExpireTimeout(1000000); + + tx.startRunning(); + + tx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + if (!tx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()) ) { + return 1; + } + + // 2 packets per second (packet duration of 500ms) + uint32 period = 500; + uint16 inc = tx.getCurrentRTPClockRate()/2; + TimerPort::setTimer(period); + uint32 i; + for (i = 0; i < pattern.getPacketsNumber(); i++ ) { + tx.putData(i*inc, + pattern.getPacketData(i), + pattern.getPacketSize(i)); + cout << "Sent some data: " << i << endl; + Thread::sleep(TimerPort::getTimer()); + TimerPort::incTimer(period); + } + tx.putData(i*inc, (unsigned char*)"exit", 5); + Thread::sleep(TimerPort::getTimer()); + return 0; + } +}; + + +class RecvPacketTransmissionTest: public Thread { +public: + void + run() { + doTest(); + } + + int + doTest() { + ExtZrtpSession rx(pattern.getSsrc()+1, pattern.getDestinationAddress(), + pattern.getDestinationPort()); + +// SymmetricZRTPSession rx(pattern.getSsrc()+1, pattern.getDestinationAddress(), +// pattern.getDestinationPort()); + rx.setSchedulingTimeout(10000); + rx.setExpireTimeout(1000000); + + rx.startRunning(); + rx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + // arbitrary number of loops to provide time to start transmitter + if (!rx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2) ) { + return 1; + } + for ( int i = 0; i < 5000 ; i++ ) { + const AppDataUnit* adu; + while ( (adu = rx.getData(rx.getFirstTimestamp())) ) { + cerr << "got some data: " << adu->getData() << endl; + if (*adu->getData() == 'e') { + delete adu; + return 0; + } + delete adu; + } + Thread::sleep(70); + } + return 0; + } +}; + + +/** + * SymmetricZRTPSession in security mode. + * + * The next two classes show how to use <code>SymmetricZRTPSession</code> + * using the standard ZRTP handshake an switching to encrypted (SRTP) mode. + * The application enables this by calling <code>initialize(...)</code>. + * Some embedded logging informs about the ZRTP processing. + */ + +class ZrtpSendPacketTransmissionTest: public Thread, public TimerPort { +public: + void + run() { + doTest(); + } + + int doTest() { + // should be valid? + //RTPSession tx(); + ExtZrtpSession tx(pattern.getSsrc(), pattern.getDestinationAddress(), + pattern.getDestinationPort()+2); + tx.initialize("test_t.zid"); + + tx.setSchedulingTimeout(10000); + tx.setExpireTimeout(1000000); + + tx.startRunning(); + + tx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + if (!tx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()) ) { + return 1; + } + tx.startZrtp(); + // 2 packets per second (packet duration of 500ms) + uint32 period = 500; + uint16 inc = tx.getCurrentRTPClockRate()/2; + TimerPort::setTimer(period); + uint32 i; + for (i = 0; i < pattern.getPacketsNumber(); i++ ) { + tx.putData(i*inc, + pattern.getPacketData(i), + pattern.getPacketSize(i)); + cout << "Sent some data: " << i << endl; + Thread::sleep(TimerPort::getTimer()); + TimerPort::incTimer(period); + } + tx.putData(i*inc, (unsigned char*)"exit", 5); + Thread::sleep(200); + return 0; + } +}; + +class ZrtpRecvPacketTransmissionTest: public Thread { +public: + void + run() { + doTest(); + } + + int + doTest() { + ExtZrtpSession rx(pattern.getSsrc()+1, pattern.getDestinationAddress(), + pattern.getDestinationPort()); + + rx.initialize("test_r.zid"); + + rx.setSchedulingTimeout(10000); + rx.setExpireTimeout(1000000); + + rx.startRunning(); + rx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + // arbitrary number of loops to provide time to start transmitter + if (!rx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2) ) { + return 1; + } + rx.startZrtp(); + for ( int i = 0; i < 5000 ; i++ ) { + const AppDataUnit* adu; + while ( (adu = rx.getData(rx.getFirstTimestamp())) ) { + cerr << "got some data: " << adu->getData() << endl; + if (*adu->getData() == 'e') { + delete adu; + return 0; + } + delete adu; + } + Thread::sleep(70); + } + return 0; + } +}; + +/** + * Simple User Callback class + * + * This class overwrite some methods from ZrtpUserCallback to get information + * about ZRTP processing and information about ZRTP results. The standard + * implementation of this class just perform return, thus effectively + * supressing any callback or trigger. + */ +class MyUserCallback: public ZrtpUserCallback { + + static map<int32, std::string*> infoMap; + static map<int32, std::string*> warningMap; + static map<int32, std::string*> severeMap; + static map<int32, std::string*> zrtpMap; + + static bool initialized; + + SymmetricZRTPSession* session; + public: + MyUserCallback(SymmetricZRTPSession* s) { + session = s; + if (initialized) { + return; + } + infoMap.insert(pair<int32, std::string*>(InfoHelloReceived, new string("Hello received, preparing a Commit"))); + infoMap.insert(pair<int32, std::string*>(InfoCommitDHGenerated, new string("Commit: Generated a public DH key"))); + infoMap.insert(pair<int32, std::string*>(InfoRespCommitReceived, new string("Responder: Commit received, preparing DHPart1"))); + infoMap.insert(pair<int32, std::string*>(InfoDH1DHGenerated, new string("DH1Part: Generated a public DH key"))); + infoMap.insert(pair<int32, std::string*>(InfoInitDH1Received, new string("Initiator: DHPart1 received, preparing DHPart2"))); + infoMap.insert(pair<int32, std::string*>(InfoRespDH2Received, new string("Responder: DHPart2 received, preparing Confirm1"))); + infoMap.insert(pair<int32, std::string*>(InfoInitConf1Received, new string("Initiator: Confirm1 received, preparing Confirm2"))); + infoMap.insert(pair<int32, std::string*>(InfoRespConf2Received, new string("Responder: Confirm2 received, preparing Conf2Ack"))); + infoMap.insert(pair<int32, std::string*>(InfoRSMatchFound, new string("At least one retained secrets matches - security OK"))); + infoMap.insert(pair<int32, std::string*>(InfoSecureStateOn, new string("Entered secure state"))); + infoMap.insert(pair<int32, std::string*>(InfoSecureStateOff, new string("No more security for this session"))); + + warningMap.insert(pair<int32, std::string*>(WarningDHAESmismatch, + new string("Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096"))); + warningMap.insert(pair<int32, std::string*>(WarningGoClearReceived, new string("Received a GoClear message"))); + warningMap.insert(pair<int32, std::string*>(WarningDHShort, + new string("Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096"))); + warningMap.insert(pair<int32, std::string*>(WarningNoRSMatch, new string("No retained secret matches - verify SAS"))); + warningMap.insert(pair<int32, std::string*>(WarningCRCmismatch, new string("Internal ZRTP packet checksum mismatch - packet dropped"))); + warningMap.insert(pair<int32, std::string*>(WarningSRTPauthError, new string("Dropping packet because SRTP authentication failed!"))); + warningMap.insert(pair<int32, std::string*>(WarningSRTPreplayError, new string("Dropping packet because SRTP replay check failed!"))); + + severeMap.insert(pair<int32, std::string*>(SevereHelloHMACFailed, new string("Hash HMAC check of Hello failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereCommitHMACFailed, new string("Hash HMAC check of Commit failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereDH1HMACFailed, new string("Hash HMAC check of DHPart1 failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereDH2HMACFailed, new string("Hash HMAC check of DHPart2 failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereCannotSend, new string("Cannot send data - connection or peer down?"))); + severeMap.insert(pair<int32, std::string*>(SevereProtocolError, new string("Internal protocol error occured!"))); + severeMap.insert(pair<int32, std::string*>(SevereNoTimer, new string("Cannot start a timer - internal resources exhausted?"))); + severeMap.insert(pair<int32, std::string*>(SevereTooMuchRetries, + new string("Too much retries during ZRTP negotiation - connection or peer down?"))); + + zrtpMap.insert(pair<int32, std::string*>(MalformedPacket, new string("Malformed packet (CRC OK, but wrong structure)"))); + zrtpMap.insert(pair<int32, std::string*>(CriticalSWError, new string("Critical software error"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppZRTPVersion, new string("Unsupported ZRTP version"))); + zrtpMap.insert(pair<int32, std::string*>(HelloCompMismatch, new string("Hello components mismatch"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppHashType, new string("Hash type not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppCiphertype, new string("Cipher type not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppPKExchange, new string("Public key exchange not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppSRTPAuthTag, new string("SRTP auth. tag not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppSASScheme, new string("SAS scheme not supported"))); + zrtpMap.insert(pair<int32, std::string*>(NoSharedSecret, new string("No shared secret available, DH mode required"))); + zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongPV, new string("DH Error: bad pvi or pvr ( == 1, 0, or p-1)"))); + zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongHVI, new string("DH Error: hvi != hashed data"))); + zrtpMap.insert(pair<int32, std::string*>(SASuntrustedMiTM, new string("Received relayed SAS from untrusted MiTM"))); + zrtpMap.insert(pair<int32, std::string*>(ConfirmHMACWrong, new string("Auth. Error: Bad Confirm pkt HMAC"))); + zrtpMap.insert(pair<int32, std::string*>(NonceReused, new string("Nonce reuse"))); + zrtpMap.insert(pair<int32, std::string*>(EqualZIDHello, new string("Equal ZIDs in Hello"))); + zrtpMap.insert(pair<int32, std::string*>(GoCleatNotAllowed, new string("GoClear packet received, but not allowed"))); + + initialized = true; + } + + void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { + string* msg; + if (sev == Info) { + msg = infoMap[subCode]; + if (msg != NULL) { + cout << *msg << endl; + } + } + if (sev == Warning) { + msg = warningMap[subCode]; + if (msg != NULL) { + cout << *msg << endl; + } + } + if (sev == Severe) { + msg = severeMap[subCode]; + if (msg != NULL) { + cout << *msg << endl; + } + } + if (sev == ZrtpError) { + if (subCode < 0) { // received an error packet from peer + subCode *= -1; + cout << "Received error packet: "; + } + else { + cout << "Sent error packet: "; + } + msg = zrtpMap[subCode]; + if (msg != NULL) { + cout << *msg << endl; + } + } + } + + void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { + string* msg; + if (sev == ZrtpError) { + if (subCode < 0) { // received an error packet from peer + subCode *= -1; + cout << "Received error packet: "; + } + else { + cout << "Sent error packet: "; + } + msg = zrtpMap[subCode]; + if (msg != NULL) { + cout << *msg << endl; + } + } + else { + msg = severeMap[subCode]; + cout << *msg << endl; + } + } + + void secureOn(std::string cipher) { + cout << "Using cipher:" << cipher << endl; + } + + void showSAS(std::string sas, bool verified) { + cout << "SAS is: " << sas << endl; + + } +}; + +map<int32, std::string*>MyUserCallback::infoMap; +map<int32, std::string*>MyUserCallback::warningMap; +map<int32, std::string*>MyUserCallback::severeMap; +map<int32, std::string*>MyUserCallback::zrtpMap; + +bool MyUserCallback::initialized = false; + +/** + * SymmetricZRTPSession in security mode and using a callback class. + * + * The next two classes show how to use <code>SymmetricZRTPSession</code> + * using the standard ZRTP handshake an switching to encrypted (SRTP) mode. + * The application enables this by calling <code>initialize(...)</code>. + * In addition the application sets a callback class (see above). ZRTP calls + * the methods of the callback class and the application may implement + * appropriate methods to deal with these triggers. + */ + +class +ZrtpSendPacketTransmissionTestCB : public Thread, public TimerPort +{ +public: + void + run() + { + doTest(); + } + + int doTest() + { + // should be valid? + //RTPSession tx(); + ExtZrtpSession tx(/*pattern.getSsrc(),*/ pattern.getDestinationAddress(), + pattern.getDestinationPort()+2); + tx.initialize("test_t.zid"); + // At this point the Hello hash is available. See ZRTP specification + // chapter 9.1 for further information when an how to use the Hello + // hash. + cout << "TX Hello hash: " << tx.getHelloHash() << endl; + cout << "TX Hello hash length: " << tx.getHelloHash().length() << endl; + + tx.setUserCallback(new MyUserCallback(&tx)); + + tx.setSchedulingTimeout(10000); + tx.setExpireTimeout(1000000); + + tx.startRunning(); + + tx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + if (!tx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()) ) { + return 1; + } + tx.startZrtp(); + + // 2 packets per second (packet duration of 500ms) + uint32 period = 500; + uint16 inc = tx.getCurrentRTPClockRate()/2; + TimerPort::setTimer(period); + uint32 i; + for (i = 0; i < pattern.getPacketsNumber(); i++ ) { + tx.putData(i*inc, + pattern.getPacketData(i), + pattern.getPacketSize(i)); + cout << "Sent some data: " << i << endl; + Thread::sleep(TimerPort::getTimer()); + TimerPort::incTimer(period); + } + tx.putData(i*inc, (unsigned char*)"exit", 5); + Thread::sleep(TimerPort::getTimer()); + return 0; + } +}; + + +class +ZrtpRecvPacketTransmissionTestCB: public Thread +{ +public: + void + run() { + doTest(); + } + + int + doTest() { + ExtZrtpSession rx( /*pattern.getSsrc()+1,*/ pattern.getDestinationAddress(), + pattern.getDestinationPort()); + + rx.initialize("test_r.zid"); + // At this point the Hello hash is available. See ZRTP specification + // chapter 9.1 for further information when an how to use the Hello + // hash. + cout << "RX Hello hash: " << rx.getHelloHash() << endl; + cout << "RX Hello hash length: " << rx.getHelloHash().length() << endl; + + rx.setUserCallback(new MyUserCallback(&rx)); + + rx.setSchedulingTimeout(10000); + rx.setExpireTimeout(1000000); + + rx.startRunning(); + rx.setPayloadFormat(StaticPayloadFormat(sptPCMU)); + // arbitrary number of loops to provide time to start transmitter + if (!rx.addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2) ) { + return 1; + } + rx.startZrtp(); + + for ( int i = 0; i < 5000 ; i++ ) { + const AppDataUnit* adu; + while ( (adu = rx.getData(rx.getFirstTimestamp())) ) { + cerr << "got some data: " << adu->getData() << endl; + if (*adu->getData() == 'e') { + delete adu; + return 0; + } + delete adu; + } + Thread::sleep(500); + } + return 0; + } +}; + + +int main(int argc, char *argv[]) +{ + int result = 0; + bool send = false; + bool recv = false; + + char c; + + /* check args */ + while (1) { + c = getopt(argc, argv, "rs"); + if (c == -1) { + break; + } + switch (c) { + case 'r': + recv = true; + break; + case 's': + send = true; + break; + default: + cerr << "Wrong Arguments, only -s and -r are accepted" << endl; + } + } + + if (send || recv) { + if (send) { + cout << "Running as sender" << endl; + } + else { + cout << "Running as receiver" << endl; + } + } + else { + cerr << "No send or receive argument specificied" << endl; + exit(1); + } + + // accept as parameter if must run as --send or --recv + +#if 0 + RecvPacketTransmissionTest *rx; + SendPacketTransmissionTest *tx; + + // run several tests in parallel threads + if ( send ) { + tx = new SendPacketTransmissionTest(); + tx->start(); + tx->join(); + } else if ( recv ) { + rx = new RecvPacketTransmissionTest(); + rx->start(); + rx->join(); + } +//#endif +//#if 0 + ZrtpRecvPacketTransmissionTest *zrx; + ZrtpSendPacketTransmissionTest *ztx; + + if ( send ) { + ztx = new ZrtpSendPacketTransmissionTest(); + ztx->start(); + ztx->join(); + } else if ( recv ) { + zrx = new ZrtpRecvPacketTransmissionTest(); + zrx->start(); + zrx->join(); + } +#endif + ZrtpRecvPacketTransmissionTestCB *zrxcb; + ZrtpSendPacketTransmissionTestCB *ztxcb; + + if ( send ) { + ztxcb = new ZrtpSendPacketTransmissionTestCB(); + ztxcb->start(); + ztxcb->join(); + } else if ( recv ) { + zrxcb = new ZrtpRecvPacketTransmissionTestCB(); + zrxcb->start(); + zrxcb->join(); + } + + exit(result); +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/demo/zrtptestMulti.cpp b/demo/zrtptestMulti.cpp new file mode 100644 index 0000000..39e20a7 --- /dev/null +++ b/demo/zrtptestMulti.cpp @@ -0,0 +1,715 @@ +// Test ZRTP extension for ccRTP +// +// Copyright (C) 2008 Werner Dittmann <Werner.Dittmann@t-online.de> +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include <cstdlib> +#include <map> +#include <libzrtpcpp/zrtpccrtp.h> +#include <libzrtpcpp/ZrtpUserCallback.h> +#include <libzrtpcpp/ZrtpConfigure.h> + +using namespace ost; +using namespace std; +using namespace GnuZrtpCodes; + +/* maybe should be by special define... +static void hexdump(const char* title, const unsigned char *s, int l) { + int n=0; + + if (s == NULL) return; + + fprintf(stderr, "%s",title); + for( ; n < l ; ++n) + { + if((n%16) == 0) + fprintf(stderr, "\n%04x",n); + fprintf(stderr, " %02x",s[n]); + } + fprintf(stderr, "\n"); +} +*/ + +class PacketsPattern +{ +public: + inline const InetHostAddress& + getDestinationAddress() const + { + return destinationAddress; + } + + inline const tpport_t + getDestinationPort() const + { + return destinationPort; + } + + uint32 + getPacketsNumber() const + { + return packetsNumber; + } + + uint32 + getSsrc() const + { + return 0xdeadbeef; + } + + const unsigned char* + getPacketData(uint32 i) + { + return data[i%2]; + } + + const size_t + getPacketSize(uint32 i) + { + return strlen((char*)data[i%2]) + 1 ; + } + +private: + static const InetHostAddress destinationAddress; + static const uint16 destinationPort = 5002; + static const uint32 packetsNumber = 10; + static const uint32 packetsSize = 12; + static const unsigned char* data[]; +}; + +const InetHostAddress PacketsPattern::destinationAddress = + InetHostAddress("localhost"); + +const unsigned char* PacketsPattern::data[] = { + (unsigned char*)"0123456789\n", + (unsigned char*)"987654321\n" +}; + +PacketsPattern pattern; + +class ZrtpRecvPacketTransmissionTestCB; +class ZrtpSendPacketTransmissionTestCB; +class MyUserCallback; +class MyUserCallbackMulti; + +static ZrtpRecvPacketTransmissionTestCB* zrxcb = NULL; +static ZrtpSendPacketTransmissionTestCB* ztxcb = NULL; + +static ZrtpRecvPacketTransmissionTestCB* zrxcbMulti = NULL; +static ZrtpSendPacketTransmissionTestCB* ztxcbMulti = NULL; + +static bool enroll = false; +static bool mitm = false; +static bool untrusted = false; +static bool sender = false; +static bool recver = false; +static bool signsas = false; + + +/** + * SymmetricZRTPSession in security mode and using a callback class. + * + * The next two classes show how to use <code>SymmetricZRTPSession</code> + * using the standard ZRTP handshake an switching to encrypted (SRTP) mode. + * The application enables this by calling <code>initialize(...)</code>. + * In addition the application sets a callback class (see above). ZRTP calls + * the methods of the callback class and the application may implement + * appropriate methods to deal with these triggers. + */ + +class + ZrtpSendPacketTransmissionTestCB : public Thread, public TimerPort { + +private: + SymmetricZRTPSession* tx; + string multiParams; + string prefix; + +public: + + ZrtpSendPacketTransmissionTestCB(): tx(NULL), multiParams("") {}; + + void run() { + doTest(); + } + + int doTest(); + + string getMultiStrParams() { + return tx->getMultiStrParams(); + } + + void setMultiStrParams(string params) { + multiParams = params; + return; + } +}; + + +class + ZrtpRecvPacketTransmissionTestCB: public Thread { + +private: + SymmetricZRTPSession* rx; + string multiParams; + string prefix; + +public: + ZrtpRecvPacketTransmissionTestCB(): rx(NULL), multiParams("") {}; + + void run() { + doTest(); + } + + int doTest(); + + string getMultiStrParams() { + return rx->getMultiStrParams(); + } + + void setMultiStrParams(string params) { + multiParams = params; + return; + } +}; + +/** + * Simple User Callback class + * + * This class overwrite some methods from ZrtpUserCallback to get information + * about ZRTP processing and information about ZRTP results. The standard + * implementation of this class just perform return, thus effectively + * supressing any callback or trigger. + */ +class MyUserCallback: public ZrtpUserCallback { + +protected: + static map<int32, std::string*> infoMap; + static map<int32, std::string*> warningMap; + static map<int32, std::string*> severeMap; + static map<int32, std::string*> zrtpMap; + static map<int32, std::string*> enrollMap; + + + static bool initialized; + + SymmetricZRTPSession* session; + + std::string prefix; + +public: + MyUserCallback(SymmetricZRTPSession* s): session(s), prefix("default: ") { + + if (initialized) { + return; + } + infoMap.insert(pair<int32, std::string*>(InfoHelloReceived, new string("Hello received, preparing a Commit"))); + infoMap.insert(pair<int32, std::string*>(InfoCommitDHGenerated, new string("Commit: Generated a public DH key"))); + infoMap.insert(pair<int32, std::string*>(InfoRespCommitReceived, new string("Responder: Commit received, preparing DHPart1"))); + infoMap.insert(pair<int32, std::string*>(InfoDH1DHGenerated, new string("DH1Part: Generated a public DH key"))); + infoMap.insert(pair<int32, std::string*>(InfoInitDH1Received, new string("Initiator: DHPart1 received, preparing DHPart2"))); + infoMap.insert(pair<int32, std::string*>(InfoRespDH2Received, new string("Responder: DHPart2 received, preparing Confirm1"))); + infoMap.insert(pair<int32, std::string*>(InfoInitConf1Received, new string("Initiator: Confirm1 received, preparing Confirm2"))); + infoMap.insert(pair<int32, std::string*>(InfoRespConf2Received, new string("Responder: Confirm2 received, preparing Conf2Ack"))); + infoMap.insert(pair<int32, std::string*>(InfoRSMatchFound, new string("At least one retained secrets matches - security OK"))); + infoMap.insert(pair<int32, std::string*>(InfoSecureStateOn, new string("Entered secure state"))); + infoMap.insert(pair<int32, std::string*>(InfoSecureStateOff, new string("No more security for this session"))); + + warningMap.insert(pair<int32, std::string*>(WarningDHAESmismatch, + new string("Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096"))); + warningMap.insert(pair<int32, std::string*>(WarningGoClearReceived, new string("Received a GoClear message"))); + warningMap.insert(pair<int32, std::string*>(WarningDHShort, + new string("Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096"))); + warningMap.insert(pair<int32, std::string*>(WarningNoRSMatch, new string("No retained secret matches - verify SAS"))); + warningMap.insert(pair<int32, std::string*>(WarningCRCmismatch, new string("Internal ZRTP packet checksum mismatch - packet dropped"))); + warningMap.insert(pair<int32, std::string*>(WarningSRTPauthError, new string("Dropping packet because SRTP authentication failed!"))); + warningMap.insert(pair<int32, std::string*>(WarningSRTPreplayError, new string("Dropping packet because SRTP replay check failed!"))); + warningMap.insert(pair<int32, std::string*>(WarningNoExpectedRSMatch, + new string("Valid retained shared secrets availabe but no matches found - must verify SAS"))); + + severeMap.insert(pair<int32, std::string*>(SevereHelloHMACFailed, new string("Hash HMAC check of Hello failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereCommitHMACFailed, new string("Hash HMAC check of Commit failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereDH1HMACFailed, new string("Hash HMAC check of DHPart1 failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereDH2HMACFailed, new string("Hash HMAC check of DHPart2 failed!"))); + severeMap.insert(pair<int32, std::string*>(SevereCannotSend, new string("Cannot send data - connection or peer down?"))); + severeMap.insert(pair<int32, std::string*>(SevereProtocolError, new string("Internal protocol error occured!"))); + severeMap.insert(pair<int32, std::string*>(SevereNoTimer, new string("Cannot start a timer - internal resources exhausted?"))); + severeMap.insert(pair<int32, std::string*>(SevereTooMuchRetries, + new string("Too much retries during ZRTP negotiation - connection or peer down?"))); + + zrtpMap.insert(pair<int32, std::string*>(MalformedPacket, new string("Malformed packet (CRC OK, but wrong structure)"))); + zrtpMap.insert(pair<int32, std::string*>(CriticalSWError, new string("Critical software error"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppZRTPVersion, new string("Unsupported ZRTP version"))); + zrtpMap.insert(pair<int32, std::string*>(HelloCompMismatch, new string("Hello components mismatch"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppHashType, new string("Hash type not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppCiphertype, new string("Cipher type not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppPKExchange, new string("Public key exchange not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppSRTPAuthTag, new string("SRTP auth. tag not supported"))); + zrtpMap.insert(pair<int32, std::string*>(UnsuppSASScheme, new string("SAS scheme not supported"))); + zrtpMap.insert(pair<int32, std::string*>(NoSharedSecret, new string("No shared secret available, DH mode required"))); + zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongPV, new string("DH Error: bad pvi or pvr ( == 1, 0, or p-1)"))); + zrtpMap.insert(pair<int32, std::string*>(DHErrorWrongHVI, new string("DH Error: hvi != hashed data"))); + zrtpMap.insert(pair<int32, std::string*>(SASuntrustedMiTM, new string("Received relayed SAS from untrusted MiTM"))); + zrtpMap.insert(pair<int32, std::string*>(ConfirmHMACWrong, new string("Auth. Error: Bad Confirm pkt HMAC"))); + zrtpMap.insert(pair<int32, std::string*>(NonceReused, new string("Nonce reuse"))); + zrtpMap.insert(pair<int32, std::string*>(EqualZIDHello, new string("Equal ZIDs in Hello"))); + zrtpMap.insert(pair<int32, std::string*>(GoCleatNotAllowed, new string("GoClear packet received, but not allowed"))); + + enrollMap.insert(pair<int32, std::string*>(EnrollmentRequest, new string("Trusted MitM enrollment requested"))); + enrollMap.insert(pair<int32, std::string*>(EnrollmentCanceled, new string("Trusted MitM enrollment canceled by user"))); + enrollMap.insert(pair<int32, std::string*>(EnrollmentFailed, new string("Trusted MitM enrollment failed"))); + enrollMap.insert(pair<int32, std::string*>(EnrollmentOk, new string("Trusted MitM enrollment OK"))); + + initialized = true; + } + + void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { + string* msg; + uint8_t sasHash[32]; + + if (sev == Info) { + msg = infoMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + // this sets up and starts off the multi-stream test + if (subCode == InfoSecureStateOn) { + if (zrxcbMulti != NULL) { + zrxcbMulti->setMultiStrParams(session->getMultiStrParams()); + zrxcbMulti->start(); + } + if (ztxcbMulti != NULL) { + ztxcbMulti->setMultiStrParams(session->getMultiStrParams()); + ztxcbMulti->start(); + } + if (sender) { + if (mitm && !enroll) { // sender now acts as trusted PBX in normal mode, not in enrollement service + std::string render = session->getSasType(); + for (int i = 0; i < 32; i++) { + sasHash[i] = 0; + } + if (untrusted) { // treat receiver as non-enrolled receiver + cout << prefix << "send SAS relay to non-enrolled receiver" << endl; + session->sendSASRelayPacket(sasHash, render); + } + else { + sasHash[0] = 0x11; + sasHash[1] = 0x22; + sasHash[2] = 0x33; + sasHash[4] = 0x44; + cout << prefix << "send SAS relay to enrolled receiver" << endl; + session->sendSASRelayPacket(sasHash, render); + } + } + } + } + } + if (sev == Warning) { + msg = warningMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + if (sev == Severe) { + msg = severeMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + if (sev == ZrtpError) { + if (subCode < 0) { // received an error packet from peer + subCode *= -1; + cout << prefix << "Received error packet: "; + } + else { + cout << prefix << "Sent error packet: "; + } + msg = zrtpMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + } + + void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { + string* msg; + if (sev == ZrtpError) { + if (subCode < 0) { // received an error packet from peer + subCode *= -1; + cout << prefix << "Received error packet: "; + } + else { + cout << prefix << "Sent error packet: "; + } + msg = zrtpMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + else { + msg = severeMap[subCode]; + cout << prefix << *msg << endl; + } + } + + void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) { + string* msg = enrollMap[info]; + cout << prefix << *msg << endl; + session->acceptEnrollment(true); + } + + void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) { + string* msg = enrollMap[info]; + cout << prefix << *msg << endl; + } + + void secureOn(std::string cipher) { + cout << prefix << "Using cipher:" << cipher << endl; + cout << prefix << "peer hello hash: " << session->getPeerHelloHash() << endl; + } + + void showSAS(std::string sas, bool verified) { + cout << prefix << "SAS is: " << sas << endl; + + } + + void signSAS(uint8_t* sasHash) { + cout << prefix << "SAS to sign" << endl; + uint8_t sign[12]; + sign[0] = sasHash[0]; + sign[1] = sasHash[1]; + sign[2] = sasHash[2]; + sign[3] = sasHash[3]; + if (recver) { + sign[4] = 'R'; + sign[5] = 'E'; + sign[6] = 'C'; + sign[7] = 'E'; + sign[8] = 'I'; + sign[9] = 'V'; + sign[10] = 'E'; + sign[11] = 'R'; + } + else { + sign[4] = 'T'; + sign[5] = 'R'; + sign[6] = 'A'; + sign[7] = 'N'; + sign[8] = 'S'; + sign[9] = 'M'; + sign[10] = 'I'; + sign[11] = 'T'; + } + cout << prefix << "set signature data result: " << session->setSignatureData(sign, 12) << endl; + } + + bool checkSASSignature(uint8_t* sasHash) { + cout << prefix << "check signature" << endl; + const uint8_t* sign = session->getSignatureData(); + cout << prefix << "signature: " << sign << endl; + return true; + } + + void setPrefix(std::string p) { + prefix = p; + } +}; + +map<int32, std::string*>MyUserCallback::infoMap; +map<int32, std::string*>MyUserCallback::warningMap; +map<int32, std::string*>MyUserCallback::severeMap; +map<int32, std::string*>MyUserCallback::zrtpMap; +map<int32, std::string*>MyUserCallback::enrollMap; + +bool MyUserCallback::initialized = false; + + +class MyUserCallbackMulti: public MyUserCallback { + +public: + + MyUserCallbackMulti(SymmetricZRTPSession* s): MyUserCallback(s) { + } + + void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) { + string* msg; + if (sev == Info) { + msg = infoMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + if (sev == Warning) { + msg = warningMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + if (sev == Severe) { + msg = severeMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + if (sev == ZrtpError) { + if (subCode < 0) { // received an error packet from peer + subCode *= -1; + cout << prefix << "Received error packet: "; + } + else { + cout << prefix << "Sent error packet: "; + } + msg = zrtpMap[subCode]; + if (msg != NULL) { + cout << prefix << *msg << endl; + } + } + } +}; + +int ZrtpSendPacketTransmissionTestCB::doTest() { + + ZrtpConfigure config; + + MyUserCallback* mcb; + if (!multiParams.empty()) { + tx = new SymmetricZRTPSession(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2+10); +// tx->initialize("test_t.zid", true, &config); + tx->initialize("test_t.zid", true); + tx->setMultiStrParams(multiParams); + + prefix = "TX Multi: "; + mcb = new MyUserCallbackMulti(tx); + mcb->setPrefix(prefix); + } + else { + tx = new SymmetricZRTPSession(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2); + //config.addHashAlgo(Sha384); +// tx->initialize("test_t.zid", true, &config); + if (mitm) { // Act as trusted MitM - could be enrolled + tx->setMitmMode(true); + } + + tx->setSignSas(signsas); + tx->initialize("test_t.zid", true); + + if (enroll) // act as PBX enrollement service + tx->setEnrollmentMode(true); + + prefix = "TX: "; + mcb = new MyUserCallback(tx); + mcb->setPrefix(prefix); + } + // At this point the Hello hash is available. See ZRTP specification + // chapter 9.1 for further information when an how to use the Hello + // hash. + cout << prefix << "Hello hash: " << tx->getHelloHash() << endl; + cout << prefix << "Hello hash length: " << tx->getHelloHash().length() << endl; + tx->setUserCallback(mcb); + tx->setSchedulingTimeout(10000); + tx->setExpireTimeout(1000000); + + tx->startRunning(); + + tx->setPayloadFormat(StaticPayloadFormat(sptPCMU)); + + if (!multiParams.empty()) { + if (!tx->addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+10) ) { + return 1; + } + } + else { + if (!tx->addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()) ) { + return 1; + } + } + tx->startZrtp(); + + // 2 packets per second (packet duration of 500ms) + uint32 period = 500; + uint16 inc = tx->getCurrentRTPClockRate()/2; + TimerPort::setTimer(period); + uint32 i; + for (i = 0; i < pattern.getPacketsNumber(); i++ ) { + tx->putData(i*inc, + pattern.getPacketData(i), + pattern.getPacketSize(i)); + cout << prefix << "Sent some data: " << i << endl; + Thread::sleep(TimerPort::getTimer()); + TimerPort::incTimer(period); + } + tx->putData(i*inc, (unsigned char*)"exit", 5); + Thread::sleep(TimerPort::getTimer()); + delete tx; + return 0; +} + + +int ZrtpRecvPacketTransmissionTestCB::doTest() { + + ZrtpConfigure config; + + MyUserCallback* mcb; + if (!multiParams.empty()) { + rx = new SymmetricZRTPSession(pattern.getDestinationAddress(), + pattern.getDestinationPort()+10); + +// rx->initialize("test_r.zid", true, &config); + rx->initialize("test_r.zid", true); + rx->setMultiStrParams(multiParams); + + prefix = "RX Multi: "; + mcb = new MyUserCallbackMulti(rx); + mcb->setPrefix(prefix); + } + else { + rx = new SymmetricZRTPSession(pattern.getDestinationAddress(), + pattern.getDestinationPort()); + config.setStandardConfig(); + if (enroll) + config.setTrustedMitM(true); // allow a trusted MitM to start enrollment process + + rx->setSignSas(signsas); + + // config.addHashAlgo(Sha384); + rx->initialize("test_r.zid", true, &config); +// rx->initialize("test_r.zid", true); + + prefix = "RX: "; + mcb = new MyUserCallback(rx); + mcb->setPrefix(prefix); + } + // At this point the Hello hash is available. See ZRTP specification + // chapter 9.1 for further information when an how to use the Hello + // hash. + cout << prefix << "Hello hash: " << rx->getHelloHash() << endl; + cout << prefix << "Hello hash length: " << rx->getHelloHash().length() << endl; + rx->setUserCallback(mcb); + rx->setSchedulingTimeout(10000); + rx->setExpireTimeout(1000000); + + rx->startRunning(); + rx->setPayloadFormat(StaticPayloadFormat(sptPCMU)); + // arbitrary number of loops to provide time to start transmitter + if (!multiParams.empty()) { + if (!rx->addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2+10) ) { + return 1; + } + } + else { + if (!rx->addDestination(pattern.getDestinationAddress(), + pattern.getDestinationPort()+2) ) { + return 1; + } + } +// rx->startZrtp(); + + for ( int i = 0; i < 5000 ; i++ ) { + const AppDataUnit* adu; + while ( (adu = rx->getData(rx->getFirstTimestamp())) ) { + cerr << prefix << "got some data: " << adu->getData() << endl; + if (*adu->getData() == 'e') { + delete adu; + delete rx; + return 0; + } + delete adu; + } + Thread::sleep(70); + } + delete rx; + return 0; +} + + +int main(int argc, char *argv[]) +{ + int result = 0; + + char c; + + /* check args */ + while (1) { + c = getopt(argc, argv, "rsSmeu"); + if (c == -1) { + break; + } + switch (c) { + case 'r': + recver = true; + break; + case 's': + sender = true; + break; + case 'm': + mitm = true; + break; + case 'e': + enroll = true; + break; + case 'u': + untrusted = true; + break; + case 'S': + signsas = true; + break; + default: + cerr << "Wrong Arguments, only -s and -r are accepted" << endl; + } + } + + if (sender || recver) { + if (sender) { + cout << "Running as sender" << endl; + } + else { + cout << "Running as receiver" << endl; + } + } + else { + cerr << "No send or receive argument specificied" << endl; + exit(1); + } + + if ( sender ) { + ztxcb = new ZrtpSendPacketTransmissionTestCB(); + ztxcbMulti = new ZrtpSendPacketTransmissionTestCB(); + ztxcb->start(); + ztxcb->join(); + ztxcbMulti->join(); + } else if ( recver ) { + zrxcb = new ZrtpRecvPacketTransmissionTestCB(); + zrxcbMulti = new ZrtpRecvPacketTransmissionTestCB(); + zrxcb->start(); + zrxcb->join(); + zrxcbMulti->join(); + } + + exit(result); +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/directive b/directive new file mode 100644 index 0000000..f76a85b --- /dev/null +++ b/directive @@ -0,0 +1,3 @@ +version: 1.1 +directory: ccrtp +filename: libzrtpcpp-2.3.3.tar.gz diff --git a/doc/Doxymini b/doc/Doxymini new file mode 100644 index 0000000..2685d6e --- /dev/null +++ b/doc/Doxymini @@ -0,0 +1,1298 @@ +# Doxyfile 1.5.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "ZRTP for ccRTP " + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be extracted +# and appear in the documentation as a namespace called 'anonymous_namespace{file}', +# where file will be replaced with the base name of the file that contains the anonymous +# namespace. By default anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../src \ + ../src/libzrtpcpp \ + ../src/libzrtpcpp/crypto + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = config.h \ + macros.h \ + namespace.h + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH +# then you must also enable this option. If you don't then doxygen will produce +# a warning and turn it on anyway + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = . + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the number +# of direct children of the root node in a graph is already larger than +# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/doc/fdl.texi b/doc/fdl.texi new file mode 100644 index 0000000..5385f32 --- /dev/null +++ b/doc/fdl.texi @@ -0,0 +1,452 @@ + +@node GNU Free Documentation License +@appendixsec GNU Free Documentation License + +@cindex FDL, GNU Free Documentation License +@center Version 1.2, November 2002 + +@display +Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warrany Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. +@end enumerate + +@page +@appendixsubsec ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with...Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/doc/fdlnotice.texi b/doc/fdlnotice.texi new file mode 100644 index 0000000..d667a36 --- /dev/null +++ b/doc/fdlnotice.texi @@ -0,0 +1,6 @@ +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 +or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the section entitled "GNU +Free Documentation License". diff --git a/doc/gpl.texi b/doc/gpl.texi new file mode 100644 index 0000000..d26233c --- /dev/null +++ b/doc/gpl.texi @@ -0,0 +1,392 @@ +@node GNU General Public License +@appendixsec GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@c This file is intended to be included in another file. + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@appendixsubsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@appendixsubsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate 0 +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@appendixsubsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) @var{yyyy} @var{name of author} + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/libzrtpcpp-config.h.cmake b/libzrtpcpp-config.h.cmake new file mode 100644 index 0000000..c8a4e14 --- /dev/null +++ b/libzrtpcpp-config.h.cmake @@ -0,0 +1,53 @@ + +/* Define to 1 if you have the <gcrypt.h> header file. */ +#cmakedefine HAVE_GCRYPT_H 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#cmakedefine HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the <openssl/aes.h> header file. */ +#cmakedefine HAVE_OPENSSL_AES_H 1 + +/* Define to 1 if you have the <openssl/bn.h> header file. */ +#cmakedefine HAVE_OPENSSL_BN_H 1 + +/* Define to 1 if you have the <openssl/sha.h> header file. */ +#cmakedefine HAVE_OPENSSL_SHA_H 1 + +/* Define to 1 if you have the <pthread.h> header file. */ +#cmakedefine HAVE_PTHREAD_H 1 + +/* Name of package */ +#define PACKAGE ${PROJECT_NAME} + +/* Version number of package */ +#define VERSION ${VERSION} + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#undef restrict +/* Work around a bug in Sun C++: it does not support _Restrict, even + though the corresponding Sun C compiler does, which causes + "#define restrict _Restrict" in the previous line. Perhaps some future + version of Sun C++ will work with _Restrict; if so, it'll probably + define __RESTRICT, just as Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +#endif + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +#undef volatile diff --git a/libzrtpcpp.pc.cmake b/libzrtpcpp.pc.cmake new file mode 100644 index 0000000..f9f5541 --- /dev/null +++ b/libzrtpcpp.pc.cmake @@ -0,0 +1,15 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +pkglibdir=${libdir}/@PACKAGE@ +includedir=@includedir@ +pkgincludedir=${includedir}/@PACKAGE@ + +Name: libzrtpcpp +Description: GNU ZRTP core library +Version: @VERSION@ +Requires: @CRYPTOBACKEND@ +Libs: -L${libdir} -l@zrtplib@ +Cflags: -I${includedir} + + diff --git a/libzrtpcpp.spec b/libzrtpcpp.spec new file mode 100644 index 0000000..fa2ef73 --- /dev/null +++ b/libzrtpcpp.spec @@ -0,0 +1,123 @@ +# +# spec file for package libzrtpcpp (Version 2.3.4) +# +# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# + +Name: libzrtpcpp +Summary: A ccrtp extension for ZRTP support +BuildRequires: gcc-c++ libopenssl-devel >= 0.9.8 pkgconfig cmake +BuildRequires: libccrtp-devel >= 2.0.0 +Version: 2.3.4 +Release: 0 +License: GPL v3 or later +Group: Development/Libraries/Other +Url: https://github.com/wernerd/ZRTPCPP +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +This library is a GPL licensed extension to the GNU RTP Stack, ccrtp, +that offers compatibility with Phil Zimmermann's zrtp/Zfone voice +encryption, and which can be directly embedded into telephony +applications. + + +%package devel +License: GPL v3 or later +Group: Development/Libraries/Other +Summary: Headers and link library for libzrtpcpp +Requires: libzrtpcpp = %{version} libccrtp-devel >= 2.0.0 + +%description devel +This package provides the header files, link libraries, and +documentation for building applications that use libzrtpcpp. + + + +%prep +%setup -q + +%build +%{__mkdir} build +cd build + +cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DSYSCONFDIR=%{_sysconfdir} \ + -DMANDIR=%{_mandir} \ + -DCMAKE_VERBOSE_MAKEFILE=TRUE \ + -DCMAKE_C_FLAGS_RELEASE:STRING="$RPM_OPT_FLAGS" \ + -DCMAKE_CXX_FLAGS_RELEASE:STRING="$RPM_OPT_FLAGS" \ + .. + +%{__make} %{?_smp_mflags} + + +%install +cd build +%{__rm} -rf %{buildroot} +make install DESTDIR=%{buildroot} + +%clean +%{__rm} -rf %{buildroot} + +%files -n libzrtpcpp +%defattr(-,root,root,0755) +%doc AUTHORS COPYING README +%{_libdir}/*.so.* + +%files devel +%defattr(-,root,root,0755) +%{_libdir}/*.so +%{_libdir}/pkgconfig/*.pc +%{_includedir}/libzrtpcpp/*.h +%dir %{_includedir}/libzrtpcpp + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%changelog +* Mon Dec 27 2010 - Werner Dittmann <werner.dittmann@t-online.de> +- Add Skein MAC authentication algorithm +- lots of documentation added (doxygen ready) +- some code cleanup + +* Sun Oct 11 2009 - Werner Dittmann <werner.dittmann@t-online.de> +- Fix multistream problem +- add DH2048 mode +- update cipher selection to match latest draft (15x) +- Test with zfone3 with Ping packet mode enabled +- some code cleanup + +* Wed Jun 24 2009 - David Sugar <dyfet@gnutelephony.org> +- Spec updated per current Fedora & CentOS policies. +- Updated release 1.4.5 has all mandatory IETF interop requirements. + +* Fri Jan 26 2009 - Werner Dittmann <werner.dittmann@t-online.de> +- Update to version 1.4.2 to support the latest ZRTP + specification draft-zimmermann-avt-zrtp-12 + +* Fri Aug 22 2008 - David Sugar <dyfet@gnutelephony.org> +- Adapted for newer library naming conventions. + +* Tue Dec 11 2007 - Werner Dittmann <werner.dittmann@t-online.de> +- this is the first spec file for version 1.x.x +- remove the .la file in devel package +- use default file atttribute instead of 755 + +* Sat Apr 18 2007 - Werner Dittmann <werner.dittmann@t-online.de> +- set version to 1.1.0 +- GNU ZRTP is compatible with the latest Zfone Beta + from April 2 2007 diff --git a/libzrtpcpp.spec.cmake b/libzrtpcpp.spec.cmake new file mode 100644 index 0000000..e72c70f --- /dev/null +++ b/libzrtpcpp.spec.cmake @@ -0,0 +1,123 @@ +# +# spec file for package libzrtpcpp (Version @VERSION@) +# +# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# + +Name: libzrtpcpp +Summary: A ccrtp extension for ZRTP support +BuildRequires: gcc-c++ @BUILD_REQ@ pkgconfig cmake +BuildRequires: libccrtp-devel >= 2.0.0 +Version: @VERSION@ +Release: 0 +License: GPL v3 or later +Group: Development/Libraries/Other +Url: https://github.com/wernerd/ZRTPCPP +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +This library is a GPL licensed extension to the GNU RTP Stack, ccrtp, +that offers compatibility with Phil Zimmermann's zrtp/Zfone voice +encryption, and which can be directly embedded into telephony +applications. + + +%package devel +License: GPL v3 or later +Group: Development/Libraries/Other +Summary: Headers and link library for libzrtpcpp +Requires: libzrtpcpp = %{version} libccrtp-devel >= 2.0.0 + +%description devel +This package provides the header files, link libraries, and +documentation for building applications that use libzrtpcpp. + + + +%prep +%setup -q + +%build +%{__mkdir} build +cd build + +cmake -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DSYSCONFDIR=%{_sysconfdir} \ + -DMANDIR=%{_mandir} \ + -DCMAKE_VERBOSE_MAKEFILE=TRUE \ + -DCMAKE_C_FLAGS_RELEASE:STRING="$RPM_OPT_FLAGS" \ + -DCMAKE_CXX_FLAGS_RELEASE:STRING="$RPM_OPT_FLAGS" \ + .. + +%{__make} %{?_smp_mflags} + + +%install +cd build +%{__rm} -rf %{buildroot} +make install DESTDIR=%{buildroot} + +%clean +%{__rm} -rf %{buildroot} + +%files -n libzrtpcpp +%defattr(-,root,root,0755) +%doc AUTHORS COPYING README +%{_libdir}/*.so.* + +%files devel +%defattr(-,root,root,0755) +%{_libdir}/*.so +%{_libdir}/pkgconfig/*.pc +%{_includedir}/libzrtpcpp/*.h +%dir %{_includedir}/libzrtpcpp + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%changelog +* Mon Dec 27 2010 - Werner Dittmann <werner.dittmann@t-online.de> +- Add Skein MAC authentication algorithm +- lots of documentation added (doxygen ready) +- some code cleanup + +* Sun Oct 11 2009 - Werner Dittmann <werner.dittmann@t-online.de> +- Fix multistream problem +- add DH2048 mode +- update cipher selection to match latest draft (15x) +- Test with zfone3 with Ping packet mode enabled +- some code cleanup + +* Wed Jun 24 2009 - David Sugar <dyfet@gnutelephony.org> +- Spec updated per current Fedora & CentOS policies. +- Updated release 1.4.5 has all mandatory IETF interop requirements. + +* Fri Jan 26 2009 - Werner Dittmann <werner.dittmann@t-online.de> +- Update to version 1.4.2 to support the latest ZRTP + specification draft-zimmermann-avt-zrtp-12 + +* Fri Aug 22 2008 - David Sugar <dyfet@gnutelephony.org> +- Adapted for newer library naming conventions. + +* Tue Dec 11 2007 - Werner Dittmann <werner.dittmann@t-online.de> +- this is the first spec file for version 1.x.x +- remove the .la file in devel package +- use default file atttribute instead of 755 + +* Sat Apr 18 2007 - Werner Dittmann <werner.dittmann@t-online.de> +- set version to 1.1.0 +- GNU ZRTP is compatible with the latest Zfone Beta + from April 2 2007 diff --git a/src/Base32.cpp b/src/Base32.cpp new file mode 100644 index 0000000..ccc8034 --- /dev/null +++ b/src/Base32.cpp @@ -0,0 +1,335 @@ +/** + * + * Copyright (c) 2002 Bryce "Zooko" Wilcox-O'Hearn Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software to + * deal in this software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of this software, and to permit persons to whom this software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of this software. + * + * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THIS SOFTWARE. + * + * Converted to C++ by: + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +#ifndef UNIT_TEST +#include <libzrtpcpp/Base32.h> +#else +#include "libzrtpcpp/Base32.h" +#endif + +int divceil(int a, int b) { + int c; + if (a>0) { + if (b>0) c=a+b-1; + else c=a; + } else { + if (b>0) c=a; + else c=a+b+1; + } + return c/b; +} + +// 1 2 3 +// 01234567890123456789012345678901 +static const char* const chars= "ybndrfg8ejkmcpqxot1uwisza345h769"; + +/* + * revchars: index into this table with the ASCII value of the char. + * The result is the value of that quintet. + */ +static const unsigned char revchars[]= { + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 18, 255, 25, 26, 27, 30, 29, + 7, 31, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 24, 1, 12, 3, 8, 5, 6, + 28, 21, 9, 10, 255, 11, 2, 16, + 13, 14, 4, 22, 17, 19, 255, 20, + 15, 0, 23, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255 +}; + + +Base32::Base32(const string encoded): + binaryResult(NULL), resultLength(0) { + + a2b_l(encoded, encoded.size(), (encoded.size()*5/8)*8); +} + +Base32::Base32(const string encoded, int noOfBits): + binaryResult(NULL), resultLength(0) { + + a2b_l(encoded, divceil(noOfBits, 5), noOfBits); +} + +Base32::Base32(const unsigned char* data, int noOfBits): + binaryResult(NULL), resultLength(0) { + + b2a_l(data, (noOfBits+7)/8, noOfBits); +} + +Base32::~Base32() { + if (binaryResult != NULL && binaryResult != smallBuffer) { + delete [] binaryResult; + } + binaryResult = NULL; +} + +const unsigned char* Base32::getDecoded(int &length) { + length = resultLength; + return binaryResult; +} + +void Base32::b2a_l(const unsigned char* os, int len, + const size_t lengthinbits) { + + /* if lengthinbits is not a multiple of 8 then this is allocating + * space for 0, 1, or 2 extra quintets that will be truncated at the + * end of this function if they are not needed + */ + string result(divceil(len*8, 5), ' '); + + /* index into the result buffer, initially pointing to the + * "one-past-the-end" quintet + */ + int resp = result.size(); + + /* pointer into the os buffer, initially pointing to the + * "one-past-the-end" octet + */ + const unsigned char* osp = os + len; + + /* Now this is a real live Duff's device. You gotta love it. */ + + unsigned long x = 0; // to hold up to 32 bits worth of the input + switch ((osp - os) % 5) { + + case 0: + do { + x = *--osp; + result[--resp] = chars[x % 32]; /* The least sig 5 bits go into the final quintet. */ + x /= 32; /* ... now we have 3 bits worth in x... */ + case 4: + x |= ((unsigned long)(*--osp)) << 3; /* ... now we have 11 bits worth in x... */ + result[--resp] = chars[x % 32]; + x /= 32; /* ... now we have 6 bits worth in x... */ + result[--resp] = chars[x % 32]; + x /= 32; /* ... now we have 1 bits worth in x... */ + case 3: + x |= ((unsigned long)(*--osp)) << 1; /* The 8 bits from the 2-indexed octet. + So now we have 9 bits worth in x... */ + result[--resp] = chars[x % 32]; + x /= 32; /* ... now we have 4 bits worth in x... */ + case 2: + x |= ((unsigned long)(*--osp)) << 4; /* The 8 bits from the 1-indexed octet. + So now we have 12 bits worth in x... */ + result[--resp] = chars[x%32]; + x /= 32; /* ... now we have 7 bits worth in x... */ + result[--resp] = chars[x%32]; + x /= 32; /* ... now we have 2 bits worth in x... */ + case 1: + x |= ((unsigned long)(*--osp)) << 2; /* The 8 bits from the 0-indexed octet. + So now we have 10 bits worth in x... */ + result[--resp] = chars[x%32]; + x /= 32; /* ... now we have 5 bits worth in x... */ + result[--resp] = chars[x]; + } while (osp > os); + } /* switch ((osp - os.buf) % 5) */ + + /* truncate any unused trailing zero quintets */ + encoded = result.substr(0, divceil(lengthinbits, 5)); + return; +} + +void Base32::a2b_l(const string cs, size_t size, const size_t lengthinbits ) { + unsigned long x = 0; // to hold up to 32 bits worth of the input + + int len = divceil(size*5, 8); + + /* if lengthinbits is not a multiple of 5 then this is + * allocating space for 0 or 1 extra octets that will be + * truncated at the end of this function if they are + * not needed + */ + + if (len < 128) { + binaryResult = smallBuffer; + } + else { + binaryResult = new unsigned char[len]; + } + + /* pointer into the result buffer, initially pointing to + * the "one-past-the-end" octet + */ + unsigned char* resp = binaryResult + len; + + /* index into the input buffer, initially pointing to the + * "one-past-the-end" character + */ + int csp = size; + + /* Now this is a real live Duff's device. You gotta love it. */ + switch (csp % 8) { + case 0: + do { + x = revchars[cs[--csp]&0xff]; /* 5 bits... */ + case 7: + x |= revchars[cs[--csp]&0xff] << 5; /* 10 bits... */ + *--resp = x % 256; + x /= 256; /* 2 bits... */ + case 6: + x |= revchars[cs[--csp]&0xff] << 2; /* 7 bits... */ + case 5: + x |= revchars[cs[--csp]&0xff] << 7; /* 12 bits... */ + *--resp = x % 256; + x /= 256; /* 4 bits... */ + case 4: + x |= revchars[cs[--csp]&0xff] << 4; /* 9 bits... */ + *--resp = x % 256; + x /= 256; /* 1 bit... */ + case 3: + x |= revchars[cs[--csp]&0xff] << 1; /* 6 bits... */ + case 2: + x |= revchars[cs[--csp]&0xff] << 6; /* 11 bits... */ + *--resp = x % 256; + x /= 256; /* 3 bits... */ + case 1: + x |= revchars[cs[--csp]&0xff] << 3; /* 8 bits... */ + *--resp = x % 256; + } while (csp); + } /* switch ((csp - cs.buf) % 8) */ + + /* truncate any unused trailing zero octets */ + resultLength = divceil(lengthinbits, 8); + return; +} + +#ifdef UNIT_TEST +#include <math.h> + + +static uint8_t *randz(const size_t len) +{ + uint8_t* result = (uint8_t*)malloc(len); + size_t i; + for (i=0; i<len; i++) { + result[i] = rand() % 256; + } + return result; +} + +int main(int argc, char *argv[]) { + + int32_t resLen; + string a; + const uint8_t* zrecovered; + uint8_t ones[] = {1, 1, 1, 1, 1}; + uint8_t zrtpVec01[] = {0x00, 0x00, 0x00, 0x00}; + uint8_t zrtpVec02[] = {0x80, 0x00, 0x00, 0x00}; + uint8_t zrtpVec03[] = {0x40, 0x00, 0x00, 0x00}; + uint8_t zrtpVec04[] = {0xc0, 0x00, 0x00, 0x00}; + uint8_t zrtpVec05[] = {0x00, 0x00, 0x00, 0x00}; + uint8_t zrtpVec06[] = {0x80, 0x80, 0x00, 0x00}; + uint8_t zrtpVec07[] = {0x8b, 0x88, 0x80, 0x00}; + uint8_t zrtpVec08[] = {0xf0, 0xbf, 0xc7, 0x00}; + uint8_t zrtpVec09[] = {0xd4, 0x7a, 0x04, 0x00}; + uint8_t zrtpVec10[] = {0xf5, 0x57, 0xbb, 0x0c}; + + // Encode all bits of the 5 one bytes (= 40 bits) + a = Base32(ones, 5*8).getEncoded(); + + // The string should be: "yryonyeb" + cout << "Encoded 5 ones: '" << a << "', Expected: 'yryonyeb'" << endl; + + // Now decode all bits and check + Base32 *y = new Base32(a); + zrecovered = y->getDecoded(resLen); + if (resLen != 5 && memcmp(ones, zrecovered, 5)) { + printf("Failed basic 5 ones recovery test.\n"); + return -1; + } + delete y; + + a = Base32(ones, 15).getEncoded(); + cout << "Encoded 5 ones, 15 bits only: '" << a << "', Expected: 'yry'" << endl; + // now decode 15 bits (out of 40 possible) + y = new Base32(a, 15); + zrecovered = y->getDecoded(resLen); + printf("Decoded 15 bits, result length: %d (should be 2)\n", resLen); + printf("Decoded bytes: %x %x (should be 1 0)\n", zrecovered[0], zrecovered[1]); + delete y; + + // Encode 20 bits of the test vectors + a = Base32(zrtpVec01, 20).getEncoded(); + cout << "Encoded ZRTP vector 01: '" << a << "', Expected: 'yyyy'" << endl; + a = Base32(zrtpVec02, 20).getEncoded(); + cout << "Encoded ZRTP vector 02: '" << a << "', Expected: 'oyyy'" << endl; + a = Base32(zrtpVec03, 20).getEncoded(); + cout << "Encoded ZRTP vector 02: '" << a << "', Expected: 'eyyy'" << endl; + a = Base32(zrtpVec04, 20).getEncoded(); + cout << "Encoded ZRTP vector 04: '" << a << "', Expected: 'ayyy'" << endl; + a = Base32(zrtpVec05, 20).getEncoded(); + cout << "Encoded ZRTP vector 05: '" << a << "', Expected: 'yyyy'" << endl; + a = Base32(zrtpVec06, 20).getEncoded(); + cout << "Encoded ZRTP vector 06: '" << a << "', Expected: 'onyy'" << endl; + a = Base32(zrtpVec07, 20).getEncoded(); + cout << "Encoded ZRTP vector 07: '" << a << "', Expected: 'tqre'" << endl; + a = Base32(zrtpVec08, 20).getEncoded(); + cout << "Encoded ZRTP vector 08: '" << a << "', Expected: '6n9h'" << endl; + a = Base32(zrtpVec09, 20).getEncoded(); + cout << "Encoded ZRTP vector 09: '" << a << "', Expected: '4t7y'" << endl; + a = Base32(zrtpVec10, 20).getEncoded(); + cout << "Encoded ZRTP vector 10: '" << a << "', Expected: '6im5'" << endl; + + // test the 30 bit output of same data as 20 bit + a = Base32(zrtpVec10, 30).getEncoded(); + cout << "Encoded ZRTP vector 10 (30bit): '" << a << "', Expected: '6im5sd'" << endl; + + for (int i = 0; i < 2; i++) { + uint8_t* z = randz(16); + a = Base32(z, 16*8).getEncoded(); +// cout << "Result: " << a << endl; + assert (a.size() == Base32::b2alen(16*8)); + zrecovered = Base32(a).getDecoded(resLen); + if (resLen != 16 && memcmp(z, zrecovered, 16)) { + printf("Failed basic recovery test.\n"); + return -1; + } + free((void*)z); + } +} +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 0000000..4febbde --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required (VERSION 2.6) + +# add_subdirectory(libzrtpcpp) +# add_subdirectory(libzrtpcpp/crypto) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +set(gcrypt_src + libzrtpcpp/crypto/gcrypt/gcryptZrtpDH.cpp + libzrtpcpp/crypto/gcrypt/gcrypthmac256.cpp + libzrtpcpp/crypto/gcrypt/gcryptsha256.cpp + libzrtpcpp/crypto/gcrypt/gcrypthmac384.cpp + libzrtpcpp/crypto/gcrypt/gcryptsha384.cpp + libzrtpcpp/crypto/gcrypt/gcryptAesCFB.cpp + libzrtpcpp/crypto/gcrypt/InitializeGcrypt.cpp) + +set(openssl_src + libzrtpcpp/crypto/openssl/ZrtpDH.cpp + libzrtpcpp/crypto/openssl/hmac256.cpp + libzrtpcpp/crypto/openssl/sha256.cpp + libzrtpcpp/crypto/openssl/hmac384.cpp + libzrtpcpp/crypto/openssl/sha384.cpp + libzrtpcpp/crypto/openssl/AesCFB.cpp + libzrtpcpp/crypto/openssl/InitializeOpenSSL.cpp) + +if (GCRYPT_FOUND) + set(crypto_src ${gcrypt_src}) +endif() + +if (OPENSSL_FOUND AND HAVE_OPENSSL_EC_H) + set(crypto_src ${openssl_src}) +endif() + +if(enable_ccrtp) + set(ccrtp_src ZrtpQueue.cpp) +endif() + +set(twofish_srcs libzrtpcpp/crypto/twofish.c + libzrtpcpp/crypto/twofish_cfb.c + libzrtpcpp/crypto/TwoCFB.cpp) + +set(zrtp_src + ZrtpCallbackWrapper.cpp + ZIDFile.cpp + ZIDRecord.cpp + Zrtp.cpp + ZrtpCrc32.cpp + ZrtpPacketCommit.cpp + ZrtpPacketConf2Ack.cpp + ZrtpPacketConfirm.cpp + ZrtpPacketDHPart.cpp + ZrtpPacketGoClear.cpp + ZrtpPacketClearAck.cpp + ZrtpPacketHelloAck.cpp + ZrtpPacketHello.cpp + ZrtpPacketError.cpp + ZrtpPacketErrorAck.cpp + ZrtpPacketPingAck.cpp + ZrtpPacketPing.cpp + ZrtpPacketSASrelay.cpp + ZrtpPacketRelayAck.cpp + ZrtpStateClass.cpp + ZrtpTextData.cpp + ZrtpConfigure.cpp + ZrtpCWrapper.cpp + Base32.cpp) + +set(zrtpcpp_src ${zrtp_src} ${ccrtp_src} ${crypto_src} ${twofish_srcs}) + +if(BUILD_STATIC AND NOT BUILD_SHARED) + set(LIBRARY_BUILD_TYPE STATIC) +else() + set(LIBRARY_BUILD_TYPE SHARED) +endif() + +add_library(${zrtplib} ${LIBRARY_BUILD_TYPE} ${zrtpcpp_src}) +set_target_properties(${zrtplib} PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSION}) +target_link_libraries(${zrtplib} ${LIBS}) + +if(enable_ccrtp) + add_dependencies(${zrtplib} ccrtp) +endif() + +add_subdirectory(libzrtpcpp) + +install(TARGETS ${zrtplib} DESTINATION ${LIBDIRNAME}) + diff --git a/src/ZIDFile.cpp b/src/ZIDFile.cpp new file mode 100644 index 0000000..69b43eb --- /dev/null +++ b/src/ZIDFile.cpp @@ -0,0 +1,430 @@ +/* + Copyright (C) 2006-2008 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ +// #define UNIT_TEST + +#include <string> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> + +#include <libzrtpcpp/ZIDFile.h> + + +static ZIDFile* instance; +static int errors = 0; // maybe we will use as member of ZIDFile later... + +void ZIDFile::createZIDFile(char* name) { + zidFile = fopen(name, "wb+"); + // New file, generate an associated random ZID and save + // it as first record + if (zidFile != NULL) { + unsigned int* ip; + ip = (unsigned int*) associatedZid; + srand(time(NULL)); + *ip++ = rand(); + *ip++ = rand(); + *ip = rand(); + + ZIDRecord rec(associatedZid); + rec.setOwnZIDRecord(); + fseek(zidFile, 0L, SEEK_SET); + if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1) + ++errors; + fflush(zidFile); + } +} + +/** + * Migrate old ZID file format to new one. + * + * If ZID file is old format: + * - close it, rename it, then re-open + * - create ZID file for new format + * - copy over contents and flags. + */ +void ZIDFile::checkDoMigration(char* name) { + FILE* fdOld; + unsigned char inb[2]; + zidrecord1_t recOld; + + fseek(zidFile, 0L, SEEK_SET); + if (fread(inb, 2, 1, zidFile) < 1) { + ++errors; + inb[0] = 0; + } + + if (inb[0] > 0) { // if it's new format just return + return; + } + fclose(zidFile); // close old ZID file + zidFile = NULL; + + // create save file name, rename and re-open + // if rename fails, just unlink old ZID file and create a brand new file + // just a little inconvenience for the user, need to verify new SAS + std::string fn = std::string(name) + std::string(".save"); + if (rename(name, fn.c_str()) < 0) { + unlink(name); + createZIDFile(name); + return; + } + fdOld = fopen(fn.c_str(), "rb"); // reopen old format in read only mode + + // Get first record from old file - is the own ZID + fseek(fdOld, 0L, SEEK_SET); + if (fread(&recOld, sizeof(zidrecord1_t), 1, fdOld) != 1) { + fclose(fdOld); + return; + } + if (recOld.ownZid != 1) { + fclose(fdOld); + return; + } + zidFile = fopen(name, "wb+"); // create new format file in binary r/w mode + if (zidFile == NULL) { + return; + } + // create ZIDRecord in new format, copy over own ZID and write the record + ZIDRecord rec(recOld.identifier); + rec.setOwnZIDRecord(); + if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1) + ++errors; + + // now copy over all valid records from old ZID file format. + // Sequentially read old records, sequentially write new records + int numRead; + do { + numRead = fread(&recOld, sizeof(zidrecord1_t), 1, fdOld); + if (numRead == 0) { // all old records processed + break; + } + // skip own ZID record and invalid records + if (recOld.ownZid == 1 || recOld.recValid == 0) { + continue; + } + ZIDRecord rec2(recOld.identifier); + rec2.setValid(); + if (recOld.rs1Valid & SASVerified) { + rec2.setSasVerified(); + } + rec2.setNewRs1(recOld.rs2Data); + rec2.setNewRs1(recOld.rs1Data); + if (fwrite(rec2.getRecordData(), rec2.getRecordLength(), 1, zidFile) < 1) + ++errors; + + } while (numRead == 1); + fflush(zidFile); +} + +ZIDFile::~ZIDFile() { + close(); +} + +ZIDFile* ZIDFile::getInstance() { + + if (instance == NULL) { + instance = new ZIDFile(); + } + return instance; +} + +int ZIDFile::open(char* name) { + + // check for an already active ZID file + if (zidFile != NULL) { + return 0; + } + if ((zidFile = fopen(name, "rb+")) == NULL) { + createZIDFile(name); + } else { + checkDoMigration(name); + if (zidFile != NULL) { + ZIDRecord rec; + fseek(zidFile, 0L, SEEK_SET); + if (fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) != 1) { + fclose(zidFile); + zidFile = NULL; + return -1; + } + if (!rec.isOwnZIDRecord()) { + fclose(zidFile); + zidFile = NULL; + return -1; + } + memcpy(associatedZid, rec.getIdentifier(), IDENTIFIER_LEN); + } + } + return ((zidFile == NULL) ? -1 : 1); +} + +void ZIDFile::close() { + + if (zidFile != NULL) { + fclose(zidFile); + zidFile = NULL; + } +} + +unsigned int ZIDFile::getRecord(ZIDRecord* zidRecord) { + unsigned long pos; + ZIDRecord rec; + int numRead; + + // set read pointer behind first record ( + fseek(zidFile, rec.getRecordLength(), SEEK_SET); + + do { + pos = ftell(zidFile); + numRead = fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile); + if (numRead == 0) { + break; + } + + // skip own ZID record and invalid records + if (rec.isOwnZIDRecord() || !rec.isValid()) { + continue; + } + + } while (numRead == 1 && + memcmp(zidRecord->getIdentifier(), rec.getIdentifier(), IDENTIFIER_LEN) != 0); + + // If we reached end of file, then no record with the ZID + // found. We need to create a new ZID record. + if (numRead == 0) { + // create new record + ZIDRecord rec1(zidRecord->getIdentifier()); + rec1.setValid(); + if (fwrite(rec1.getRecordData(), rec1.getRecordLength(), 1, zidFile) < 1) + ++errors; + memcpy(zidRecord->getRecordData(), rec1.getRecordData(), rec1.getRecordLength()); + } else { + // Copy the read data into caller's the record storage + memcpy(zidRecord->getRecordData(), rec.getRecordData(), rec.getRecordLength()); + } + + // remember position of record in file for save operation + zidRecord->setPosition(pos); + return 1; +} + +unsigned int ZIDFile::saveRecord(ZIDRecord *zidRecord) { + + fseek(zidFile, zidRecord->getPosition(), SEEK_SET); + if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1) + ++errors; + fflush(zidFile); + return 1; +} + + +#ifdef UNIT_TEST + +#include <iostream> +#include <unistd.h> +using namespace std; + +static void hexdump(const char* title, const unsigned char *s, int l) { + int n=0; + + if (s == NULL) return; + + fprintf(stderr, "%s",title); + for (; n < l ; ++n) { + if ((n%16) == 0) + fprintf(stderr, "\n%04x",n); + fprintf(stderr, " %02x",s[n]); + } + fprintf(stderr, "\n"); +} + +int main(int argc, char *argv[]) { + + unsigned char myId[IDENTIFIER_LEN]; + ZIDFile *zid = ZIDFile::getInstance(); + + unlink("testzid2"); + zid->open("testzid2"); + hexdump("My ZID: ", zid->getZid(), IDENTIFIER_LEN); + memcpy(myId, zid->getZid(), IDENTIFIER_LEN); + zid->close(); + + zid->open("testzid2"); + if (memcmp(myId, zid->getZid(), IDENTIFIER_LEN) != 0) { + cerr << "Wrong ZID in testfile" << endl; + return 1; + } + + // Create a new ZID record for peer ZID "123456789012" + ZIDRecord zr3((unsigned char*) "123456789012"); + zid->getRecord(&zr3); + if (!zr3.isValid()) { + cerr << "New ZID record '123456789012' not set to valid" << endl; + return 1; + } + zid->saveRecord(&zr3); + + // create a second record with peer ZID "210987654321" + ZIDRecord zr4((unsigned char*) "210987654321"); + zid->getRecord(&zr4); + if (!zr4.isValid()) { + cerr << "New ZID record '210987654321' not set to valid" << endl; + return 1; + } + zid->saveRecord(&zr4); + + // now set a first RS1 with default expiration interval, check + // if set correctly, valid flag and expiration interval + zr3.setNewRs1((unsigned char*) "11122233344455566677788899900012"); + if (memcmp(zr3.getRs1(), "11122233344455566677788899900012", RS_LENGTH) != 0) { + cerr << "RS1 was not set (111...012)" << endl; + return 1; + } + if (!zr3.isRs1Valid()) { + cerr << "RS1 was not set to valid state (111...012)" << endl; + return 1; + } + if (!zr3.isRs1NotExpired()) { + cerr << "RS1 expired (111...012)" << endl; + return 1; + } + if (zr3.isRs2Valid()) { + cerr << "RS2 was set to valid state (111...012)" << endl; + return 1; + } + zid->saveRecord(&zr3); + + // create a second RS1, RS2 will become the first RS1, check + // if set correctly, valid flag and expiration interval for both + // RS1 and RS2 + zr3.setNewRs1((unsigned char*) "00099988877766655544433322211121"); + if (memcmp(zr3.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) { + cerr << "RS1 was not set (000...121)" << endl; + return 1; + } + if (!zr3.isRs1Valid()) { + cerr << "RS1 was not set to valid state (000...121)" << endl; + return 1; + } + if (!zr3.isRs1NotExpired()) { + cerr << "RS1 expired (000...121)" << endl; + return 1; + } + if (memcmp(zr3.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) { + cerr << "RS2 was not set (111...012)" << endl; + return 1; + } + if (!zr3.isRs2Valid()) { + cerr << "RS2 was not set to valid state (111...012)" << endl; + return 1; + } + if (!zr3.isRs2NotExpired()) { + cerr << "RS2 expired (111...012)" << endl; + return 1; + } + zid->saveRecord(&zr3); + + zid->close(); + + // Reopen, check if first record is still valid, RSx vaild and + // not expired. Then manipulate 2nd record. + zid->open("testzid2"); + + ZIDRecord zr3a((unsigned char*) "123456789012"); + zid->getRecord(&zr3a); + if (!zr3a.isValid()) { + cerr << "Re-read ZID record '123456789012' not set to valid" << endl; + return 1; + } + if (memcmp(zr3a.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) { + cerr << "re-read RS1 was not set (000...121)" << endl; + return 1; + } + if (!zr3a.isRs1Valid()) { + cerr << "Re-read RS1 was not set to valid state (000...121)" << endl; + return 1; + } + if (!zr3a.isRs1NotExpired()) { + cerr << "re-read RS1 expired (000...121)" << endl; + return 1; + } + if (memcmp(zr3a.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) { + cerr << "re-read RS2 was not set (111...012)" << endl; + return 1; + } + if (!zr3a.isRs2Valid()) { + cerr << "Re-read RS2 was not set to valid state (111...012)" << endl; + return 1; + } + if (!zr3a.isRs2NotExpired()) { + cerr << "Re-read RS2 expired (111...012)" << endl; + return 1; + } + + ZIDRecord zr5((unsigned char*) "210987654321"); + zid->getRecord(&zr5); + + + // set new RS1 with expire interval of 5 second, then check immediatly + zr5.setNewRs1((unsigned char*) "aaa22233344455566677788899900012", 5); + if (!zr5.isValid()) { + cerr << "Re-read ZID record '210987654321' not set to valid" << endl; + return 1; + } + if (memcmp(zr5.getRs1(), "aaa22233344455566677788899900012", RS_LENGTH) != 0) { + cerr << "RS1 (2) was not set (aaa...012)" << endl; + return 1; + } + if (!zr5.isRs1Valid()) { + cerr << "RS1 (2) was not set to valid state (aaa...012)" << endl; + return 1; + } + if (!zr5.isRs1NotExpired()) { + cerr << "RS1 (2) expired (aaa...012)" << endl; + return 1; + } + + // wait for 6 second, now the expire check shall fail + sleep(6); + if (zr5.isRs1NotExpired()) { + cerr << "RS1 (2) is not expired after defined interval (aaa...012)" << endl; + return 1; + } + + zr5.setNewRs1((unsigned char*) "bbb99988877766655544433322211121", 256); + zid->saveRecord(&zr5); + + zid->close(); + + // Test migration + zid->open("testzidOld"); + zid->close(); + +} + +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/ZIDRecord.cpp b/src/ZIDRecord.cpp new file mode 100644 index 0000000..d62c2e3 --- /dev/null +++ b/src/ZIDRecord.cpp @@ -0,0 +1,113 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <time.h> + +#include <libzrtpcpp/ZIDRecord.h> + +void ZIDRecord::setNewRs1(const unsigned char* data, int32_t expire) { + + // shift RS1 data into RS2 position + memcpy(record.rs2Data, record.rs1Data, RS_LENGTH); + memcpy(record.rs2Interval, record.rs1Interval, TIME_LENGTH); + + // now propagate flags as well + if (isRs1Valid()) { + setRs2Valid(); + } + + // set new RS1 data + memcpy(record.rs1Data, data, RS_LENGTH); + + time_t validThru; + if (expire == -1) { + validThru = -1; + } + else if (expire <= 0) { + validThru = 0; + } + else { + validThru = time(NULL) + expire; + } + + if (sizeof(time_t) == 4) { + long long temp = validThru; + memcpy(record.rs1Interval, (unsigned char*)&temp, TIME_LENGTH); + } + else { + memcpy(record.rs1Interval, (unsigned char*)&validThru, TIME_LENGTH); + } + setRs1Valid(); +} + + +const bool ZIDRecord::isRs1NotExpired() { + time_t current = time(NULL); + time_t validThru; + + if (sizeof(time_t) == 4) { + long long temp; + memcpy((unsigned char*)&temp, record.rs1Interval, TIME_LENGTH); + validThru = temp; + } + else { + memcpy((unsigned char*)&validThru, record.rs1Interval, TIME_LENGTH); + } + + if (validThru == -1) + return true; + if (validThru == 0) + return false; + return (current <= validThru) ? true : false; +} + +const bool ZIDRecord::isRs2NotExpired() { + time_t current = time(NULL); + time_t validThru; + + if (sizeof(time_t) == 4) { + long long temp; + memcpy((unsigned char*)&temp, record.rs2Interval, TIME_LENGTH); + validThru = temp; + } + else { + memcpy((unsigned char*)&validThru, record.rs2Interval, TIME_LENGTH); + } + + if (validThru == -1) + return true; + if (validThru == 0) + return false; + return (current <= validThru) ? true : false; +} + +void ZIDRecord::setMiTMData(const unsigned char* data) { + memcpy(record.mitmKey, data, RS_LENGTH); + setMITMKeyAvailable(); +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/Zrtp.cpp b/src/Zrtp.cpp new file mode 100644 index 0000000..2002462 --- /dev/null +++ b/src/Zrtp.cpp @@ -0,0 +1,2527 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/*F + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ +#include <sstream> + +#include <libzrtpcpp/crypto/ZrtpDH.h> +#include <libzrtpcpp/crypto/hmac256.h> +#include <libzrtpcpp/crypto/sha256.h> +#include <libzrtpcpp/crypto/hmac384.h> +#include <libzrtpcpp/crypto/sha384.h> +#include <libzrtpcpp/crypto/aesCFB.h> +#include <libzrtpcpp/crypto/twoCFB.h> + +#include <libzrtpcpp/ZRtp.h> +#include <libzrtpcpp/ZrtpStateClass.h> +#include <libzrtpcpp/ZIDFile.h> +#include <libzrtpcpp/ZIDRecord.h> +#include <libzrtpcpp/Base32.h> + +using namespace GnuZrtpCodes; + +/* disabled...but used in testing and debugging, probably should have a + controlling #define... + * +static void hexdump(const char* title, const unsigned char *s, int l) { + int n=0; + + if (s == NULL) return; + + fprintf(stderr, "%s",title); + for( ; n < l ; ++n) + { + if((n%16) == 0) + fprintf(stderr, "\n%04x",n); + fprintf(stderr, " %02x",s[n]); + } + fprintf(stderr, "\n"); +} + */ + +/* + * This method simplifies detection of libzrtpcpp inside Automake, configure + * and friends + */ +#ifdef __cplusplus +extern "C" { +#endif + int ZrtpAvailable() + { + return 1; + } +#ifdef __cplusplus +} +#endif + +ZRtp::ZRtp(uint8_t *myZid, ZrtpCallback *cb, std::string id, ZrtpConfigure* config, bool mitmm, bool sasSignSupport): + callback(cb), dhContext(NULL), DHss(NULL), auxSecret(NULL), auxSecretLength(0), rs1Valid(false), + rs2Valid(false), msgShaContext(NULL), multiStream(false), multiStreamAvailable(false), pbxSecretTmp(NULL), + configureAlgos(*config) { + + enableMitmEnrollment = config->isTrustedMitM(); + paranoidMode = config->isParanoidMode(); + + // setup the implicit hash function pointers and length + hashLengthImpl = SHA256_DIGEST_LENGTH; + hashFunctionImpl = sha256; + hashListFunctionImpl = sha256; + + hmacFunctionImpl = hmac_sha256; + hmacListFunctionImpl = hmac_sha256; + + /* + * Generate H0 as a random number (256 bits, 32 bytes) and then + * the hash chain, refer to chapter 9. Use the implicit hash function. + */ + randomZRTP(H0, HASH_IMAGE_SIZE); + sha256(H0, HASH_IMAGE_SIZE, H1); // hash H0 and generate H1 + sha256(H1, HASH_IMAGE_SIZE, H2); // H2 + sha256(H2, HASH_IMAGE_SIZE, H3); // H3 + + zrtpHello.configureHello(&configureAlgos); + zrtpHello.setH3(H3); // set H3 in Hello, included in helloHash + + memcpy(zid, myZid, ZID_SIZE); + zrtpHello.setZid(zid); + + if (mitmm) // this session acts for a trusted MitM (PBX) + zrtpHello.setMitmMode(); + + if (sasSignSupport) // the application supports SAS signing + zrtpHello.setSasSign(); + + setClientId(id); // set id, compute HMAC and final helloHash + + stateEngine = new ZrtpStateClass(this); +} + +ZRtp::~ZRtp() { + stopZrtp(); + if (DHss != NULL) { + delete DHss; + DHss = NULL; + } + if (stateEngine != NULL) { + delete stateEngine; + stateEngine = NULL; + } + if (dhContext != NULL) { + delete dhContext; + dhContext = NULL; + } + if (msgShaContext != NULL) { + closeHashCtx(msgShaContext, NULL); + msgShaContext = NULL; + } + if (auxSecret != NULL) { + delete auxSecret; + auxSecret = NULL; + auxSecretLength = 0; + } + memset(hmacKeyI, 0, MAX_DIGEST_LENGTH); + memset(hmacKeyR, 0, MAX_DIGEST_LENGTH); + + memset(zrtpKeyI, 0, MAX_DIGEST_LENGTH); + memset(zrtpKeyR, 0, MAX_DIGEST_LENGTH); + /* + * Clear the Initiator's srtp key and salt + */ + memset(srtpKeyI, 0, MAX_DIGEST_LENGTH); + memset(srtpSaltI, 0, MAX_DIGEST_LENGTH); + /* + * Clear he Responder's srtp key and salt + */ + memset(srtpKeyR, 0, MAX_DIGEST_LENGTH); + memset(srtpSaltR, 0, MAX_DIGEST_LENGTH); + + memset(zrtpSession, 0, MAX_DIGEST_LENGTH); +} + +void ZRtp::processZrtpMessage(uint8_t *message, uint32_t pSSRC) { + Event_t ev; + + peerSSRC = pSSRC; + ev.type = ZrtpPacket; + ev.packet = message; + + if (stateEngine != NULL) { + stateEngine->processEvent(&ev); + } +} + +void ZRtp::processTimeout() { + Event_t ev; + + ev.type = Timer; + if (stateEngine != NULL) { + stateEngine->processEvent(&ev); + } +} + +#ifdef oldgoclear +bool ZRtp::handleGoClear(uint8_t *message) +{ + char *msg, first, last; + + msg = (char *)message + 4; + first = tolower(*msg); + last = tolower(*(msg+6)); + + if (first == 'g' && last == 'r') { + Event_t ev; + + ev.type = ZrtpGoClear; + ev.packet = message; + if (stateEngine != NULL) { + stateEngine->processEvent(&ev); + } + return true; + } + else { + return false; + } +} +#endif + +void ZRtp::startZrtpEngine() { + Event_t ev; + + if (stateEngine != NULL && stateEngine->inState(Initial)) { + ev.type = ZrtpInitial; + stateEngine->processEvent(&ev); + } +} + +void ZRtp::stopZrtp() { + Event_t ev; + + if (stateEngine != NULL) { + ev.type = ZrtpClose; + stateEngine->processEvent(&ev); + } +} + +bool ZRtp::inState(int32_t state) +{ + if (stateEngine != NULL) { + return stateEngine->inState(state); + } + else { + return false; + } +} + +ZrtpPacketHello* ZRtp::prepareHello() { + return &zrtpHello; +} + +ZrtpPacketHelloAck* ZRtp::prepareHelloAck() { + return &zrtpHelloAck; +} + +/* + * At this point we will assume the role of Initiator. This role may change + * in case we have a commit-clash. Refer to chapter 5.2 in the spec how + * to break this tie. + */ +ZrtpPacketCommit* ZRtp::prepareCommit(ZrtpPacketHello *hello, uint32_t* errMsg) { + + sendInfo(Info, InfoHelloReceived); + + if (memcmp(hello->getVersion(), zrtpVersion, ZRTP_WORD_SIZE-1) != 0) { + *errMsg = UnsuppZRTPVersion; + return NULL; + } + // Save our peer's (presumably the Responder) ZRTP id + memcpy(peerZid, hello->getZid(), ZID_SIZE); + if (memcmp(peerZid, zid, ZID_SIZE) == 0) { // peers have same ZID???? + *errMsg = EqualZIDHello; + return NULL; + } + memcpy(peerH3, hello->getH3(), HASH_IMAGE_SIZE); + + /* + * The Following section extracts the algorithm from the peer's Hello + * packet. Always the preferend offered algorithms are + * used. If the received Hello does not contain algo specifiers + * or offers only unsupported optional algos then replace + * these with mandatory algos and put them into the Commit packet. + * Refer to the findBest*() functions. + * If this is a MultiStream ZRTP object then do not get the cipher, + * authentication from hello packet but use the pre-initialized values + * as proposed by the standard. If we switch to responder mode the + * commit packet may contain other algos - see function + * prepareConfirm2MultiStream(...). + */ + sasType = findBestSASType(hello); + + if (!multiStream) { + authLength = findBestAuthLen(hello); + pubKey = findBestPubkey(hello); + cipher = findBestCipher(hello, pubKey); + hash = findBestHash(hello); + multiStreamAvailable = checkMultiStream(hello); + } + else { + if (checkMultiStream(hello)) { + return prepareCommitMultiStream(hello); + } + else { + // we are in multi-stream but peer does not offer multi-stream + // return error code to other party - unsupported PK, must be Mult + *errMsg = UnsuppPKExchange; + return NULL; + } + } + setNegotiatedHash(hash); + + // Modify here when introducing new DH key agreement, for example + // elliptic curves. + dhContext = new ZrtpDH(pubKey->getName()); + dhContext->generatePublicKey(); + + dhContext->getPubKeyBytes(pubKeyBytes); + sendInfo(Info, InfoCommitDHGenerated); + + // Prepare IV data that we will use during confirm packet encryption. + randomZRTP(randomIV, sizeof(randomIV)); + + /* + * Prepare our DHPart2 packet here. Required to compute HVI. If we stay + * in Initiator role then we reuse this packet later in prepareDHPart2(). + * To create this DH packet we have to compute the retained secret ids + * first. Thus get our peer's retained secret data first. + */ + ZIDRecord zidRec(peerZid); + ZIDFile *zidFile = ZIDFile::getInstance(); + zidFile->getRecord(&zidRec); + + //Compute the Initator's and Responder's retained secret ids. + computeSharedSecretSet(zidRec); + + // Check if a PBX application set the MitM flag. + if (hello->isMitmMode()) { + mitmSeen = true; + } + // Flag to record that fact that we have a MitM key of the other peer. + peerIsEnrolled = zidRec.isMITMKeyAvailable(); + + signSasSeen = hello->isSasSign(); + // Construct a DHPart2 message (Initiator's DH message). This packet + // is required to compute the HVI (Hash Value Initiator), refer to + // chapter 5.4.1.1. + + // Fill the values in the DHPart2 packet + zrtpDH2.setPubKeyType(pubKey->getName()); + zrtpDH2.setMessageType((uint8_t*)DHPart2Msg); + zrtpDH2.setRs1Id(rs1IDi); + zrtpDH2.setRs2Id(rs2IDi); + zrtpDH2.setAuxSecretId(auxSecretIDi); + zrtpDH2.setPbxSecretId(pbxSecretIDi); + zrtpDH2.setPv(pubKeyBytes); + zrtpDH2.setH1(H1); + + int32_t len = zrtpDH2.getLength() * ZRTP_WORD_SIZE; + + // Compute HMAC over DH2, excluding the HMAC field (HMAC_SIZE) + // and store in DH2. Key to HMAC is H0, use HASH_IMAGE_SIZE bytes only. + // Must use implicit HMAC functions. + uint8_t hmac[IMPL_MAX_DIGEST_LENGTH]; + uint32_t macLen; + hmacFunctionImpl(H0, HASH_IMAGE_SIZE, (uint8_t*)zrtpDH2.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen); + zrtpDH2.setHMAC(hmac); + + // Compute the HVI, refer to chapter 5.4.1.1 of the specification + computeHvi(&zrtpDH2, hello); + + zrtpCommit.setZid(zid); + zrtpCommit.setHashType((uint8_t*)hash->getName()); + zrtpCommit.setCipherType((uint8_t*)cipher->getName()); + zrtpCommit.setAuthLen((uint8_t*)authLength->getName()); + zrtpCommit.setPubKeyType((uint8_t*)pubKey->getName()); + zrtpCommit.setSasType((uint8_t*)sasType->getName()); + zrtpCommit.setHvi(hvi); + zrtpCommit.setH2(H2); + + len = zrtpCommit.getLength() * ZRTP_WORD_SIZE; + + // Compute HMAC over Commit, excluding the HMAC field (HMAC_SIZE) + // and store in Hello. Key to HMAC is H1, use HASH_IMAGE_SIZE bytes only. + // Must use implicit HMAC functions. + hmacFunctionImpl(H1, HASH_IMAGE_SIZE, (uint8_t*)zrtpCommit.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen); + zrtpCommit.setHMAC(hmac); + + // hash first messages to produce overall message hash + // First the Responder's Hello message, second the Commit (always Initator's). + // Must use negotiated hash. + int32_t helloLen = hello->getLength() * ZRTP_WORD_SIZE; + msgShaContext = createHashCtx(); + hashCtxFunction(msgShaContext, (unsigned char*)hello->getHeaderBase(), helloLen); + hashCtxFunction(msgShaContext, (unsigned char*)zrtpCommit.getHeaderBase(), len); + + // store Hello data temporarily until we can check HMAC after receiving Commit as + // Responder or DHPart1 as Initiator + storeMsgTemp(hello); + + // calculate hash over the received Hello packet - is peer's hello hash. + // Use implicit hash algorithm + hashFunctionImpl((unsigned char*)hello->getHeaderBase(), helloLen, peerHelloHash); + memcpy(peerHelloVersion, hello->getVersion(), ZRTP_WORD_SIZE); + peerHelloVersion[ZRTP_WORD_SIZE] = 0; + + return &zrtpCommit; +} + +ZrtpPacketCommit* ZRtp::prepareCommitMultiStream(ZrtpPacketHello *hello) { + + randomZRTP(hvi, ZRTP_WORD_SIZE*4); // This is the Multi-Stream NONCE size + + zrtpCommit.setZid(zid); + zrtpCommit.setHashType((uint8_t*)hash->getName()); + zrtpCommit.setCipherType((uint8_t*)cipher->getName()); + zrtpCommit.setAuthLen((uint8_t*)authLength->getName()); + zrtpCommit.setPubKeyType((uint8_t*)"Mult"); // this is fixed because of Multi Stream mode + zrtpCommit.setSasType((uint8_t*)sasType->getName()); + zrtpCommit.setNonce(hvi); + zrtpCommit.setH2(H2); + + int32_t len = zrtpCommit.getLength() * ZRTP_WORD_SIZE; + + // Compute HMAC over Commit, excluding the HMAC field (HMAC_SIZE) + // and store in Hello. Key to HMAC is H1, use HASH_IMAGE_SIZE bytes only. + // Must use the implicit HMAC function. + uint8_t hmac[IMPL_MAX_DIGEST_LENGTH]; + uint32_t macLen; + hmacFunctionImpl(H1, HASH_IMAGE_SIZE, (uint8_t*)zrtpCommit.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen); + zrtpCommit.setHMACMulti(hmac); + + + // hash first messages to produce overall message hash + // First the Responder's Hello message, second the Commit + // (always Initator's). + // Must use the negotiated hash. + msgShaContext = createHashCtx(); + + int32_t helloLen = hello->getLength() * ZRTP_WORD_SIZE; + hashCtxFunction(msgShaContext, (unsigned char*)hello->getHeaderBase(), helloLen); + hashCtxFunction(msgShaContext, (unsigned char*)zrtpCommit.getHeaderBase(), len); + + // store Hello data temporarily until we can check HMAC after receiving Commit as + // Responder or DHPart1 as Initiator + storeMsgTemp(hello); + + // calculate hash over the received Hello packet - is peer's hello hash. + // Use implicit hash algorithm + hashFunctionImpl((unsigned char*)hello->getHeaderBase(), helloLen, peerHelloHash); + memcpy(peerHelloVersion, hello->getVersion(), ZRTP_WORD_SIZE); + peerHelloVersion[ZRTP_WORD_SIZE] = 0; + + return &zrtpCommit; +} + +/* + * At this point we will take the role of the Responder. We may have been in + * the role of the Initiator before and already sent a commit packet that + * clashed with a commit packet from our peer. If our HVI was lower than our + * peer's HVI then we switched to Responder and handle our peer's commit packet + * here. This method takes care to delete and refresh data left over from a + * possible Initiator preparation. This belongs to prepared DH data, message + * hash SHA context + */ +ZrtpPacketDHPart* ZRtp::prepareDHPart1(ZrtpPacketCommit *commit, uint32_t* errMsg) { + + sendInfo(Info, InfoRespCommitReceived); + + // The following code check the hash chain according chapter 10 to detect + // false ZRTP packets. + // Must use the implicit hash function. + uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH]; + memcpy(peerH2, commit->getH2(), HASH_IMAGE_SIZE); + hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpH3); + + if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) { + *errMsg = IgnorePacket; + return NULL; + } + + // Check HMAC of previous Hello packet stored in temporary buffer. The + // HMAC key of peer's Hello packet is peer's H2 that is contained in the + // Commit packet. Refer to chapter 9.1. + if (!checkMsgHmac(peerH2)) { + sendInfo(Severe, SevereHelloHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + + // check if we support the commited Cipher type + AlgorithmEnum* cp = &zrtpSymCiphers.getByName((const char*)commit->getCipherType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppCiphertype; + return NULL; + } + cipher = cp; + + // check if we support the commited Authentication length + cp = &zrtpAuthLengths.getByName((const char*)commit->getAuthLen()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppSRTPAuthTag; + return NULL; + } + authLength = cp; + + // check if we support the commited hash type + cp = &zrtpHashes.getByName((const char*)commit->getHashType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppHashType; + return NULL; + } + // check if the peer's commited hash is the same that we used when + // preparing our commit packet. If not do the necessary resets and + // recompute some data. + if (*(int32_t*)(hash->getName()) != *(int32_t*)(cp->getName())) { + hash = cp; + setNegotiatedHash(hash); + + ZIDRecord zidRec(peerZid); + ZIDFile *zidFile = ZIDFile::getInstance(); + zidFile->getRecord(&zidRec); + + // Compute the Initator's and Responder's retained secret ids + // with the committed hash. + computeSharedSecretSet(zidRec); + } + + // check if we support the commited pub key type + cp = &zrtpPubKeys.getByName((const char*)commit->getPubKeysType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppPKExchange; + return NULL; + } + pubKey = cp; + + // check if we support the commited SAS type + cp = &zrtpSasTypes.getByName((const char*)commit->getSasType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppSASScheme; + return NULL; + } + sasType = cp; + + // dhContext cannot be NULL - always setup during prepareCommit() + // check if we can use the dhContext prepared by prepareCOmmit(), + // if not delete old DH context and generate new one + // The algorithm names are 4 chars only, thus we can cast to int32_t + if (*(int32_t*)(dhContext->getDHtype()) != *(int32_t*)(pubKey->getName())) { + delete dhContext; + dhContext = new ZrtpDH(pubKey->getName()); + dhContext->generatePublicKey(); + } + sendInfo(Info, InfoDH1DHGenerated); + + dhContext->getPubKeyBytes(pubKeyBytes); + + // Setup a DHPart1 packet. + zrtpDH1.setPubKeyType(pubKey->getName()); + zrtpDH1.setMessageType((uint8_t*)DHPart1Msg); + zrtpDH1.setRs1Id(rs1IDr); + zrtpDH1.setRs2Id(rs2IDr); + zrtpDH1.setAuxSecretId(auxSecretIDr); + zrtpDH1.setPbxSecretId(pbxSecretIDr); + zrtpDH1.setPv(pubKeyBytes); + zrtpDH1.setH1(H1); + + int32_t len = zrtpDH1.getLength() * ZRTP_WORD_SIZE; + + // Compute HMAC over DHPart1, excluding the HMAC field (HMAC_SIZE) + // and store in DHPart1. + // Use implicit Hash function + uint8_t hmac[IMPL_MAX_DIGEST_LENGTH]; + uint32_t macLen; + hmacFunctionImpl(H0, HASH_IMAGE_SIZE, (uint8_t*)zrtpDH1.getHeaderBase(), len-(HMAC_SIZE), hmac, &macLen); + zrtpDH1.setHMAC(hmac); + + // We are definitly responder. Save the peer's hvi for later compare. + myRole = Responder; + memcpy(peerHvi, commit->getHvi(), HVI_SIZE); + + // We are responder. Release a possibly pre-computed SHA context + // because this was prepared for Initiator. Then create a new one. + if (msgShaContext != NULL) { + closeHashCtx(msgShaContext, NULL); + } + msgShaContext = createHashCtx(); + + // Hash messages to produce overall message hash: + // First the Responder's (my) Hello message, second the Commit + // (always Initator's), then the DH1 message (which is always a + // Responder's message). + // Must use negotiated hash + hashCtxFunction(msgShaContext, (unsigned char*)zrtpHello.getHeaderBase(), zrtpHello.getLength() * ZRTP_WORD_SIZE); + hashCtxFunction(msgShaContext, (unsigned char*)commit->getHeaderBase(), commit->getLength() * ZRTP_WORD_SIZE); + hashCtxFunction(msgShaContext, (unsigned char*)zrtpDH1.getHeaderBase(), zrtpDH1.getLength() * ZRTP_WORD_SIZE); + + // store Commit data temporarily until we can check HMAC after we got DHPart2 + storeMsgTemp(commit); + + return &zrtpDH1; +} + +/* + * At this point we will take the role of the Initiator. + */ +ZrtpPacketDHPart* ZRtp::prepareDHPart2(ZrtpPacketDHPart *dhPart1, uint32_t* errMsg) { + + uint8_t* pvr; + + sendInfo(Info, InfoInitDH1Received); + + // Because we are initiator the protocol engine didn't receive Commit + // thus could not store a peer's H2. A two step SHA256 is required to + // re-compute H3. Then compare with peer's H3 from peer's Hello packet. + // Must use implicit hash function. + uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH]; + hashFunctionImpl(dhPart1->getH1(), HASH_IMAGE_SIZE, tmpHash); // Compute peer's H2 + memcpy(peerH2, tmpHash, HASH_IMAGE_SIZE); + hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpHash); // Compute peer's H3 (tmpHash) + + if (memcmp(tmpHash, peerH3, HASH_IMAGE_SIZE) != 0) { + *errMsg = IgnorePacket; + return NULL; + } + + // Check HMAC of previous Hello packet stored in temporary buffer. The + // HMAC key of the Hello packet is peer's H2 that was computed above. + // Refer to chapter 9.1 and chapter 10. + if (!checkMsgHmac(peerH2)) { + sendInfo(Severe, SevereHelloHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + + // get memory to store DH result TODO: make it fixed memory + DHss = new uint8_t[dhContext->getDhSize()]; + if (DHss == NULL) { + *errMsg = CriticalSWError; + return NULL; + } + + // get and check Responder's public value, see chap. 5.4.3 in the spec + pvr = dhPart1->getPv(); + if (!dhContext->checkPubKey(pvr)) { + *errMsg = DHErrorWrongPV; + return NULL; + } + dhContext->computeSecretKey(pvr, DHss); + + myRole = Initiator; + + // We are Initiator: the Responder's Hello and the Initiator's (our) Commit + // are already hashed in the context. Now hash the Responder's DH1 and then + // the Initiator's (our) DH2 in that order. + // Use the negotiated hash function. + hashCtxFunction(msgShaContext, (unsigned char*)dhPart1->getHeaderBase(), dhPart1->getLength() * ZRTP_WORD_SIZE); + hashCtxFunction(msgShaContext, (unsigned char*)zrtpDH2.getHeaderBase(), zrtpDH2.getLength() * ZRTP_WORD_SIZE); + + // Compute the message Hash + closeHashCtx(msgShaContext, messageHash); + msgShaContext = NULL; + + // To compute the keys for the Initiator we need the retained secrets of our + // peer. Get them from the storage. + ZIDRecord zidRec(peerZid); + ZIDFile *zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + // Now compute the S0, all dependend keys and the new RS1. The function + // also performs sign SAS callback if it's active. + generateKeysInitiator(dhPart1, zidRec); + zid->saveRecord(&zidRec); + + delete dhContext; + dhContext = NULL; + + // TODO: at initiator we can call signSAS at this point, don't dealy until confirm1 reveived + // store DHPart1 data temporarily until we can check HMAC after receiving Confirm1 + storeMsgTemp(dhPart1); + return &zrtpDH2; +} + +/* + * At this point we are Responder. + */ +ZrtpPacketConfirm* ZRtp::prepareConfirm1(ZrtpPacketDHPart* dhPart2, uint32_t* errMsg) { + + uint8_t* pvi; + + sendInfo(Info, InfoRespDH2Received); + + // Because we are responder we received a Commit and stored its H2. + // Now re-compute H2 from received H1 and compare with stored peer's H2. + // Use implicit hash function + uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH]; + hashFunctionImpl(dhPart2->getH1(), HASH_IMAGE_SIZE, tmpHash); + if (memcmp(tmpHash, peerH2, HASH_IMAGE_SIZE) != 0) { + *errMsg = IgnorePacket; + return NULL; + } + + // Check HMAC of Commit packet stored in temporary buffer. The + // HMAC key of the Commit packet is peer's H1 that is contained in + // DHPart2. Refer to chapter 9.1 and chapter 10. + if (!checkMsgHmac(dhPart2->getH1())) { + sendInfo(Severe, SevereCommitHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + // Now we have the peer's pvi. Because we are responder re-compute my hvi + // using my Hello packet and the Initiator's DHPart2 and compare with + // hvi sent in commit packet. If it doesn't macht then a MitM attack + // may have occured. + computeHvi(dhPart2, &zrtpHello); + if (memcmp(hvi, peerHvi, HVI_SIZE) != 0) { + *errMsg = DHErrorWrongHVI; + return NULL; + } + DHss = new uint8_t[dhContext->getDhSize()]; + if (DHss == NULL) { + *errMsg = CriticalSWError; + return NULL; + } + // Get and check the Initiator's public value, see chap. 5.4.2 of the spec + pvi = dhPart2->getPv(); + if (!dhContext->checkPubKey(pvi)) { + *errMsg = DHErrorWrongPV; + return NULL; + } + dhContext->computeSecretKey(pvi, DHss); + // Hash the Initiator's DH2 into the message Hash (other messages already + // prepared, see method prepareDHPart1(). + // Use neotiated hash function + hashCtxFunction(msgShaContext, (unsigned char*)dhPart2->getHeaderBase(), dhPart2->getLength() * ZRTP_WORD_SIZE); + + closeHashCtx(msgShaContext, messageHash); + msgShaContext = NULL; + + // To compute the Keys for the Initiator we need the retained secrets of our + // peer. Get them from the storage. + ZIDRecord zidRec(peerZid); + ZIDFile *zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + /* + * The expected shared secret Ids were already computed when we built the + * DHPart1 packet. Generate s0, all depended keys, and the new RS1 value + * for the ZID record. The functions also performs sign SAS callback if it's active. + */ + generateKeysResponder(dhPart2, zidRec); + zid->saveRecord(&zidRec); + + delete dhContext; + dhContext = NULL; + + // Fill in Confirm1 packet. + zrtpConfirm1.setMessageType((uint8_t*)Confirm1Msg); + + // Check if user verfied the SAS in a previous call and thus verfied + // the retained secret. Don't set the verified flag if paranoidMode is true. + if (zidRec.isSasVerified() && !paranoidMode) { + zrtpConfirm1.setSASFlag(); + } + zrtpConfirm1.setExpTime(0xFFFFFFFF); + zrtpConfirm1.setIv(randomIV); + zrtpConfirm1.setHashH0(H0); + + // if this run at PBX user agent enrollment service then set flag in confirm + // packet and store the MitM key + if (enrollmentMode) { + computePBXSecret(); + zrtpConfirm1.setPBXEnrollment(); + writeEnrollmentPBX(); + } + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + // Encrypt and HMAC with Responder's key - we are Respondere here + int hmlen = (zrtpConfirm1.getLength() - 9) * ZRTP_WORD_SIZE; + cipher->getEncrypt()(zrtpKeyR, cipher->getKeylen(), randomIV, zrtpConfirm1.getHashH0(), hmlen); + hmacFunction(hmacKeyR, hashLength, (unsigned char*)zrtpConfirm1.getHashH0(), hmlen, confMac, &macLen); + + zrtpConfirm1.setHmac(confMac); + + // store DHPart2 data temporarily until we can check HMAC after receiving Confirm2 + storeMsgTemp(dhPart2); + return &zrtpConfirm1; +} + +/* + * At this point we are Responder. + */ +ZrtpPacketConfirm* ZRtp::prepareConfirm1MultiStream(ZrtpPacketCommit* commit, uint32_t* errMsg) { + + sendInfo(Info, InfoRespCommitReceived); + + // The following code checks the hash chain according chapter 10 to detect + // false ZRTP packets. + // Use implicit hash function + uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH]; + memcpy(peerH2, commit->getH2(), HASH_IMAGE_SIZE); + hashFunctionImpl(peerH2, HASH_IMAGE_SIZE, tmpH3); + + if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) { + *errMsg = IgnorePacket; + return NULL; + } + + // Check HMAC of previous Hello packet stored in temporary buffer. The + // HMAC key of peer's Hello packet is peer's H2 that is contained in the + // Commit packet. Refer to chapter 9.1. + if (!checkMsgHmac(peerH2)) { + sendInfo(Severe, SevereHelloHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + + // check if Commit contains "Mult" as pub key type + AlgorithmEnum* cp = &zrtpPubKeys.getByName((const char*)commit->getPubKeysType()); + if (!cp->isValid() || *(int32_t*)(cp->getName()) != *(int32_t*)mult) { + *errMsg = UnsuppPKExchange; + return NULL; + } + + // check if we support the commited cipher + cp = &zrtpSymCiphers.getByName((const char*)commit->getCipherType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppCiphertype; + return NULL; + } + cipher = cp; + + // check if we support the commited Authentication length + cp = &zrtpAuthLengths.getByName((const char*)commit->getAuthLen()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppSRTPAuthTag; + return NULL; + } + authLength = cp; + + // check if we support the commited hash type + cp = &zrtpHashes.getByName((const char*)commit->getHashType()); + if (!cp->isValid()) { // no match - something went wrong + *errMsg = UnsuppHashType; + return NULL; + } + // check if the peer's commited hash is the same that we used when + // preparing our commit packet. If not do the necessary resets and + // recompute some data. + if (*(int32_t*)(hash->getName()) != *(int32_t*)(cp->getName())) { + hash = cp; + setNegotiatedHash(hash); + } + myRole = Responder; + + // We are responder. Release a possibly pre-computed SHA256 context + // because this was prepared for Initiator. Then create a new one. + if (msgShaContext != NULL) { + closeHashCtx(msgShaContext, NULL); + } + msgShaContext = createHashCtx(); + + // Hash messages to produce overall message hash: + // First the Responder's (my) Hello message, second the Commit + // (always Initator's) + // use negotiated hash + hashCtxFunction(msgShaContext, (unsigned char*)zrtpHello.getHeaderBase(), zrtpHello.getLength() * ZRTP_WORD_SIZE); + hashCtxFunction(msgShaContext, (unsigned char*)commit->getHeaderBase(), commit->getLength() * ZRTP_WORD_SIZE); + + closeHashCtx(msgShaContext, messageHash); + msgShaContext = NULL; + + generateKeysMultiStream(); + + // Fill in Confirm1 packet. + zrtpConfirm1.setMessageType((uint8_t*)Confirm1Msg); + zrtpConfirm1.setExpTime(0xFFFFFFFF); + zrtpConfirm1.setIv(randomIV); + zrtpConfirm1.setHashH0(H0); + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + // Encrypt and HMAC with Responder's key - we are Respondere here + int32_t hmlen = (zrtpConfirm1.getLength() - 9) * ZRTP_WORD_SIZE; + cipher->getEncrypt()(zrtpKeyR, cipher->getKeylen(), randomIV, zrtpConfirm1.getHashH0(), hmlen); + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyR, hashLength, (unsigned char*)zrtpConfirm1.getHashH0(), hmlen, confMac, &macLen); + + zrtpConfirm1.setHmac(confMac); + + // Store Commit data temporarily until we can check HMAC after receiving Confirm2 + storeMsgTemp(commit); + return &zrtpConfirm1; +} + +/* + * At this point we are Initiator. + */ +ZrtpPacketConfirm* ZRtp::prepareConfirm2(ZrtpPacketConfirm* confirm1, uint32_t* errMsg) { + + sendInfo(Info, InfoInitConf1Received); + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + // Use the Responder's keys here because we are Initiator here and + // receive packets from Responder + int16_t hmlen = (confirm1->getLength() - 9) * ZRTP_WORD_SIZE; + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyR, hashLength, (unsigned char*)confirm1->getHashH0(), hmlen, confMac, &macLen); + + if (memcmp(confMac, confirm1->getHmac(), HMAC_SIZE) != 0) { + *errMsg = ConfirmHMACWrong; + return NULL; + } + cipher->getDecrypt()(zrtpKeyR, cipher->getKeylen(), confirm1->getIv(), confirm1->getHashH0(), hmlen); + + std::string cs(cipher->getReadable()); + cs.append("/").append(pubKey->getName()); + + // Check HMAC of DHPart1 packet stored in temporary buffer. The + // HMAC key of the DHPart1 packet is peer's H0 that is contained in + // Confirm1. Refer to chapter 9. + if (!checkMsgHmac(confirm1->getHashH0())) { + sendInfo(Severe, SevereDH1HMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + signatureLength = confirm1->getSignatureLength(); + if (signSasSeen && signatureLength > 0) { + signatureData = confirm1->getSignatureData(); + callback->checkSASSignature(sasHash); + // TODO: error handling if checkSASSignature returns false. + } + /* + * The Confirm1 is ok, handle the Retained secret stuff and inform + * GUI about state. + */ + bool sasFlag = confirm1->isSASFlag(); + + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + + ZIDFile *zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + // Our peer did not confirm the SAS in last session, thus reset + // our SAS flag too. Reset the flag also if paranoidMode is true. + if (!sasFlag || paranoidMode) { + zidRec.resetSasVerified(); + } + // get verified flag from current RS1 before set a new RS1. This + // may not be set even if peer's flag is set in confirm1 message. + sasFlag = zidRec.isSasVerified(); + + callback->srtpSecretsOn(cs, SAS, sasFlag); + + // now we are ready to save the new RS1 which inherits the verified + // flag from old RS1 + zidRec.setNewRs1((const uint8_t*)newRs1); + zid->saveRecord(&zidRec); + + // now generate my Confirm2 message + zrtpConfirm2.setMessageType((uint8_t*)Confirm2Msg); + zrtpConfirm2.setHashH0(H0); + + if (sasFlag) { + zrtpConfirm2.setSASFlag(); + } + zrtpConfirm2.setExpTime(0xFFFFFFFF); + zrtpConfirm2.setIv(randomIV); + + // Compute PBX secret if we are in enrollemnt mode (PBX user agent) + // or enrollment was enabled at normal user agent and flag in confirm packet + if (enrollmentMode || (enableMitmEnrollment && confirm1->isPBXEnrollment())) { + computePBXSecret(); + + // if this runs at PBX user agent enrollment service then set flag in confirm + // packet and store the MitM key. The PBX user agent service always stores + // its MitM key. + if (enrollmentMode) { + zrtpConfirm2.setPBXEnrollment(); + writeEnrollmentPBX(); + } + } + // Encrypt and HMAC with Initiator's key - we are Initiator here + hmlen = (zrtpConfirm2.getLength() - 9) * ZRTP_WORD_SIZE; + cipher->getEncrypt()(zrtpKeyI, cipher->getKeylen(), randomIV, zrtpConfirm2.getHashH0(), hmlen); + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyI, hashLength, (unsigned char*)zrtpConfirm2.getHashH0(), hmlen, confMac, &macLen); + + zrtpConfirm2.setHmac(confMac); + + // Ask for enrollment only if enabled via configuration and the + // confirm1 packet contains the enrollment flag. The enrolling user + // agent stores the MitM key only if the user accepts the enrollment + // request. + if (enableMitmEnrollment && confirm1->isPBXEnrollment()) { + callback->zrtpAskEnrollment(EnrollmentRequest); + } + return &zrtpConfirm2; +} + +/** + * Save the computed MitM secret to the ZID record of the peer + */ +void ZRtp::writeEnrollmentPBX() { + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + + ZIDFile *zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + if (pbxSecretTmp != NULL) { + zidRec.setMiTMData(pbxSecretTmp); + } + zid->saveRecord(&zidRec); +} + +/* + * At this point we are Initiator. + */ +ZrtpPacketConfirm* ZRtp::prepareConfirm2MultiStream(ZrtpPacketConfirm* confirm1, uint32_t* errMsg) { + + // check Confirm1 packet using the keys + // prepare Confirm2 packet + // don't update SAS, RS + sendInfo(Info, InfoInitConf1Received); + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + closeHashCtx(msgShaContext, messageHash); + msgShaContext = NULL; + myRole = Initiator; + + generateKeysMultiStream(); + + // Use the Responder's keys here because we are Initiator here and + // receive packets from Responder + int32_t hmlen = (confirm1->getLength() - 9) * ZRTP_WORD_SIZE; + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyR, hashLength, (unsigned char*)confirm1->getHashH0(), hmlen, confMac, &macLen); + + if (memcmp(confMac, confirm1->getHmac(), HMAC_SIZE) != 0) { + *errMsg = ConfirmHMACWrong; + return NULL; + } + cipher->getDecrypt()(zrtpKeyR, cipher->getKeylen(), confirm1->getIv(), confirm1->getHashH0(), hmlen); + std::string cs(cipher->getReadable()); + + // Because we are initiator the protocol engine didn't receive Commit and + // because we are using multi-stream mode here we also did not receive a DHPart1 and + // thus could not store a responder's H2 or H1. A two step hash is required to + // re-compute H1, H2. + // USe implicit hash function. + uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH]; + hashFunctionImpl(confirm1->getHashH0(), HASH_IMAGE_SIZE, tmpHash); // Compute peer's H1 in tmpHash + hashFunctionImpl(tmpHash, HASH_IMAGE_SIZE, tmpHash); // Compute peer's H2 in tmpHash + memcpy(peerH2, tmpHash, HASH_IMAGE_SIZE); // copy and truncate to peerH2 + + // Check HMAC of previous Hello packet stored in temporary buffer. The + // HMAC key of the Hello packet is peer's H2 that was computed above. + // Refer to chapter 9.1 and chapter 10. + if (!checkMsgHmac(peerH2)) { + sendInfo(Severe, SevereHelloHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + // TODO: here we have a SAS signature from reponder, call checkSASsignature (save / compare in case of resend) + + // Inform GUI about security state, don't show SAS and its state + std::string cs1(""); + callback->srtpSecretsOn(cs, cs1, true); + + // now generate my Confirm2 message + zrtpConfirm2.setMessageType((uint8_t*)Confirm2Msg); + zrtpConfirm2.setHashH0(H0); + zrtpConfirm2.setExpTime(0xFFFFFFFF); + zrtpConfirm2.setIv(randomIV); + + // Encrypt and HMAC with Initiator's key - we are Initiator here + hmlen = (zrtpConfirm2.getLength() - 9) * ZRTP_WORD_SIZE; + cipher->getEncrypt()(zrtpKeyI, cipher->getKeylen(), randomIV, zrtpConfirm2.getHashH0(), hmlen); + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyI, hashLength, (unsigned char*)zrtpConfirm2.getHashH0(), hmlen, confMac, &macLen); + + zrtpConfirm2.setHmac(confMac); + return &zrtpConfirm2; +} + +/* + * At this point we are Responder. + */ +ZrtpPacketConf2Ack* ZRtp::prepareConf2Ack(ZrtpPacketConfirm *confirm2, uint32_t* errMsg) { + + sendInfo(Info, InfoRespConf2Received); + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + // Use the Initiator's keys here because we are Responder here and + // reveice packets from Initiator + int16_t hmlen = (confirm2->getLength() - 9) * ZRTP_WORD_SIZE; + + // Use negotiated HMAC (hash) + hmacFunction(hmacKeyI, hashLength, + (unsigned char*)confirm2->getHashH0(), + hmlen, confMac, &macLen); + + if (memcmp(confMac, confirm2->getHmac(), HMAC_SIZE) != 0) { + *errMsg = ConfirmHMACWrong; + return NULL; + } + cipher->getDecrypt()(zrtpKeyI, cipher->getKeylen(), confirm2->getIv(), confirm2->getHashH0(), hmlen); + + std::string cs(cipher->getReadable()); + + if (!multiStream) { + // Check HMAC of DHPart2 packet stored in temporary buffer. The + // HMAC key of the DHPart2 packet is peer's H0 that is contained in + // Confirm2. Refer to chapter 9.1 and chapter 10. + if (!checkMsgHmac(confirm2->getHashH0())) { + sendInfo(Severe, SevereDH2HMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + signatureLength = confirm2->getSignatureLength(); + if (signSasSeen && signatureLength > 0) { + signatureData = confirm2->getSignatureData(); + callback->checkSASSignature(sasHash); + // TODO: error handling if checkSASSignature returns false. + } + /* + * The Confirm2 is ok, handle the Retained secret stuff and inform + * GUI about state. + */ + bool sasFlag = confirm2->isSASFlag(); + + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + + ZIDFile *zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + // Our peer did not confirm the SAS in last session, thus reset + // our SAS flag too. Reset the flag also if paranoidMode is true. + if (!sasFlag || paranoidMode) { + zidRec.resetSasVerified(); + } + + // Now get the resulting SAS verified flag from current RS1 before setting a new RS1. + // It's a combination of our SAS verfied flag and peer's verified flag. Only if both + // were set (true) then sasFlag becomes true. + sasFlag = zidRec.isSasVerified(); + cs.append("/").append(pubKey->getName()); + callback->srtpSecretsOn(cs, SAS, sasFlag); + + // save new RS1, this inherits the verified flag from old RS1 + zidRec.setNewRs1((const uint8_t*)newRs1); + zid->saveRecord(&zidRec); + + // Ask for enrollment only if enabled via configuration and the + // confirm packet contains the enrollment flag. The enrolling user + // agent stores the MitM key only if the user accepts the enrollment + // request. + if (enableMitmEnrollment && confirm2->isPBXEnrollment()) { + computePBXSecret(); + callback->zrtpAskEnrollment(EnrollmentRequest); + } + } + else { + // Check HMAC of Commit packet stored in temporary buffer. The + // HMAC key of the Commit packet is initiator's H1 + // use implicit hash function. + uint8_t tmpHash[IMPL_MAX_DIGEST_LENGTH]; + hashFunctionImpl(confirm2->getHashH0(), HASH_IMAGE_SIZE, tmpHash); // Compute initiator's H1 in tmpHash + + if (!checkMsgHmac(tmpHash)) { + sendInfo(Severe, SevereCommitHMACFailed); + *errMsg = CriticalSWError; + return NULL; + } + std::string cs1(""); + + // Inform GUI about security state, don't show SAS and its state + callback->srtpSecretsOn(cs, cs1, true); + } + return &zrtpConf2Ack; +} + +ZrtpPacketErrorAck* ZRtp::prepareErrorAck(ZrtpPacketError* epkt) { + sendInfo(ZrtpError, epkt->getErrorCode() * -1); + return &zrtpErrorAck; +} + +ZrtpPacketError* ZRtp::prepareError(uint32_t errMsg) { + zrtpError.setErrorCode(errMsg); + return &zrtpError; +} + +ZrtpPacketPingAck* ZRtp::preparePingAck(ZrtpPacketPing* ppkt) { + if (ppkt->getLength() != 6) // A PING packet must have a length of 6 words + return NULL; + // Because we do not support ZRTP proxy mode use the truncated ZID. + // If this code shall be used in ZRTP proxy implementation the computation + // of the endpoint hash must be enhanced (see chaps 5.15ff and 5.16) + zrtpPingAck.setLocalEpHash(zid); + zrtpPingAck.setRemoteEpHash(ppkt->getEpHash()); + zrtpPingAck.setSSRC(peerSSRC); + return &zrtpPingAck; +} + +ZrtpPacketRelayAck* ZRtp::prepareRelayAck(ZrtpPacketSASrelay* srly, uint32_t* errMsg) { + // handle and render SAS relay data only if the peer announced that it is a trusted + // PBX. Don't handle SAS relay in paranoidMode. + if (!mitmSeen || paranoidMode) + return &zrtpRelayAck; + + uint8_t* hkey, *ekey; + // If we are responder then the PBX used it's Initiator keys + if (myRole == Responder) { + hkey = hmacKeyI; + ekey = zrtpKeyI; + } + else { + hkey = hmacKeyR; + ekey = zrtpKeyR; + } + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + + // Use the Initiator's keys here because we are Responder here and + // reveice packets from Initiator + int16_t hmlen = (srly->getLength() - 9) * ZRTP_WORD_SIZE; + + // Use negotiated HMAC (hash) + hmacFunction(hkey, hashLength, (unsigned char*)srly->getFiller(), hmlen, confMac, &macLen); + + if (memcmp(confMac, srly->getHmac(), HMAC_SIZE) != 0) { + *errMsg = ConfirmHMACWrong; + return NULL; // TODO - check error handling + } + cipher->getDecrypt()(ekey, cipher->getKeylen(), srly->getIv(), (uint8_t*)srly->getFiller(), hmlen); + + const uint8_t* render = srly->getSas(); + const uint8_t* newSasHash = srly->getTrustedSas(); + + bool sasHashNull = true; + for (int i = 0; i < HASH_IMAGE_SIZE; i++) { + if (newSasHash[i] != 0) { + sasHashNull = false; + break; + } + } + // Check if new SAS is null or a trusted MitM relationship doesn't exist. + // If this is the case then don't render and don't show the new SAS - use + // the computed SAS hash but we may use a different SAS rendering algorithm to + // render the computed SAS. + if (sasHashNull || !peerIsEnrolled) { + newSasHash = sasHash; + } + // If other SAS schemes required - check here and use others + AlgorithmEnum* renderAlgo = &zrtpSasTypes.getByName((const char*)render); + uint8_t sasBytes[4];; + if (renderAlgo->isValid()) { + sasBytes[0] = newSasHash[0]; + sasBytes[1] = newSasHash[1]; + sasBytes[2] = newSasHash[2] & 0xf0; + sasBytes[3] = 0; + } + SAS = Base32(sasBytes, 20).getEncoded(); + std::string cs(cipher->getReadable()); + cs.append("/").append(pubKey->getName()).append("/MitM"); + + callback->srtpSecretsOn(cs, SAS, false); + return &zrtpRelayAck; +} + +// TODO Implement GoClear handling +ZrtpPacketClearAck* ZRtp::prepareClearAck(ZrtpPacketGoClear* gpkt) { + sendInfo(Warning, WarningGoClearReceived); + return &zrtpClearAck; +} + +ZrtpPacketGoClear* ZRtp::prepareGoClear(uint32_t errMsg) { + ZrtpPacketGoClear* gclr = &zrtpGoClear; + gclr->clrClearHmac(); + return gclr; +} + +/* + * The next functions look up and return a prefered algorithm. These + * functions work as follows: + * - If the Hello packet does not contain an algorithm (number of algorithms +* is zero) then return the mandatory algorithm. + * - Build a list of algorithm names and ids from configuration data. If + * the configuration data does not contain a mandatory algorithm append + * the mandatory algorithm to the list and ids. + * - Build a list of algorithm names from the Hello message. If + * the Hello message does not contain a mandatory algorithm append + * the mandatory algorithm to the list. + * - Lookup a matching algorithm. The list built from Hello takes + * precedence in the lookup (indexed by the outermost loop). + * + * This guarantees that we always return a supported alogrithm respecting + * the order of algorithms in the Hello message + * + * The mandatory algorithms are: (internal enums are our prefered algoritms) + * Hash: S256 (SHA 256) (internal enum Sha256) + * Symmetric Cipher: AES1 (AES 128) (internal enum Aes128) + * SRTP Authentication: HS32 and HS80 (32/80 bits) (internal enum AuthLen32) + * Key Agreement: DH3k (3072 Diffie-Helman) (internal enum Dh3072) + * + */ +AlgorithmEnum* ZRtp::findBestHash(ZrtpPacketHello *hello) { + + int i; + int ii; + int numAlgosOffered; + AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1]; + + int numAlgosConf; + AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1]; + + bool mandatoryFound = false; + + // If Hello does not contain any hash names return Sha256, its mandatory + int num = hello->getNumHashes(); + if (num == 0) { + return &zrtpHashes.getByName(mandatoryHash); + } + // Build list of configured hash algorithm names, append mandatory algos + // if necessary. + numAlgosConf = configureAlgos.getNumConfiguredAlgos(HashAlgorithm); + for (i = 0; i < numAlgosConf; i++) { + algosConf[i] = &configureAlgos.getAlgoAt(HashAlgorithm, i); + if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryHash) { + mandatoryFound = true; + } + } + if (!mandatoryFound) { + algosConf[numAlgosConf++] = &zrtpHashes.getByName(mandatoryHash); + } + + // Build list of offered known algos in Hello, append mandatory algos if necessary + mandatoryFound = false; + for (numAlgosOffered = 0, i = 0; i < num; i++) { + algosOffered[numAlgosOffered] = &zrtpHashes.getByName((const char*)hello->getHashType(i)); + if (!algosOffered[numAlgosOffered]->isValid()) + continue; + if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryHash) { + mandatoryFound = true; + } + } + if (!mandatoryFound) { + algosOffered[numAlgosOffered++] = &zrtpHashes.getByName(mandatoryHash); + } + + // Lookup offered algos in configured algos. Because of appended + // mandatory algorithms at least one match will happen + for (i = 0; i < numAlgosOffered; i++) { + for (ii = 0; ii < numAlgosConf; ii++) { + if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) { + return algosConf[ii]; + } + } + } + return &zrtpHashes.getByName(mandatoryHash); +} + +AlgorithmEnum* ZRtp::findBestCipher(ZrtpPacketHello *hello, AlgorithmEnum* pk) { + + int i; + int ii; + int numAlgosOffered; + AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1]; + + int numAlgosConf; + AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1]; + + bool mandatoryFound = false; + + int num = hello->getNumCiphers(); + if (num == 0 || (*(int32_t*)(pk->getName()) == *(int32_t*)dh2k)) { + return &zrtpSymCiphers.getByName(aes1); + } + + // Build list of configured cipher algorithm names, append mandatory algos + // if necessary. + numAlgosConf = configureAlgos.getNumConfiguredAlgos(CipherAlgorithm); + for (i = 0; i < numAlgosConf; i++) { + algosConf[i] = &configureAlgos.getAlgoAt(CipherAlgorithm, i); + if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryCipher) { + mandatoryFound = true; + } + } + if (!mandatoryFound) { + algosConf[numAlgosConf++] = &zrtpSymCiphers.getByName(mandatoryCipher); + } + + // Build list of offered known algos names in Hello, append mandatory algos if + // necessary + mandatoryFound = false; + for (numAlgosOffered = 0, i = 0; i < num; i++) { + algosOffered[numAlgosOffered] = &zrtpSymCiphers.getByName((const char*)hello->getCipherType(i)); + if (!algosOffered[numAlgosOffered]->isValid()) + continue; + if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryCipher) { + mandatoryFound = true; + } + } + + if (!mandatoryFound) { + algosOffered[numAlgosOffered++] = &zrtpSymCiphers.getByName(mandatoryCipher); + } + + // Lookup offered algos in configured algos. Because of appended + // mandatory algorithms at least one match will happen + for (i = 0; i < numAlgosOffered; i++) { + for (ii = 0; ii < numAlgosConf; ii++) { + if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) { + return algosConf[ii]; + } + } + } + return &zrtpSymCiphers.getByName(mandatoryCipher); +} + +AlgorithmEnum* ZRtp::findBestPubkey(ZrtpPacketHello *hello) { + + int i; + int ii; + int numAlgosOffered; + AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1]; + + int numAlgosConf; + AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1]; + + bool mandatoryFound = false; + + int num = hello->getNumPubKeys(); + if (num == 0) { + return &zrtpPubKeys.getByName(mandatoryPubKey); + } + // Build list of configured pubkey algorithm names, append mandatory algos + // if necessary. + // The list must include real public key algorithms only, so skip + // mult-stream mode, preshared and alike. + numAlgosConf = configureAlgos.getNumConfiguredAlgos(PubKeyAlgorithm); + for (i = 0, ii = 0; i < numAlgosConf; i++) { + algosConf[ii] = &configureAlgos.getAlgoAt(PubKeyAlgorithm, ii); + if (*(int32_t*)(algosConf[ii]->getName()) == *(int32_t*)mult) { + continue; // skip multi-stream mode + } + if (*(int32_t*)(algosConf[ii++]->getName()) == *(int32_t*)mandatoryPubKey) { + mandatoryFound = true; + } + } + + numAlgosConf = ii; + if (!mandatoryFound) { + algosConf[numAlgosConf++] = &zrtpPubKeys.getByName(mandatoryPubKey); + } + + // Build list of offered known algos in Hello, append mandatory algos if necessary + mandatoryFound = false; + for (numAlgosOffered = 0, i = 0; i < num; i++) { + algosOffered[numAlgosOffered] = &zrtpPubKeys.getByName((const char*)hello->getPubKeyType(i)); + if (!algosOffered[numAlgosOffered]->isValid()) + continue; + if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryPubKey) { + mandatoryFound = true; + } + } + + if (!mandatoryFound) { + algosOffered[numAlgosOffered++] = &zrtpPubKeys.getByName(mandatoryPubKey); + } + + // Lookup offered algos in configured algos. Because of appended + // mandatory algorithms at least one match will happen + for (i = 0; i < numAlgosOffered; i++) { + for (ii = 0; ii < numAlgosConf; ii++) { + if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) { + return algosConf[ii]; + } + } + } + return &zrtpPubKeys.getByName(mandatoryPubKey); +} + +AlgorithmEnum* ZRtp::findBestSASType(ZrtpPacketHello *hello) { + + int i; + int ii; + int numAlgosOffered; + AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+1]; + + int numAlgosConf; + AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+1]; + + bool mandatoryFound = false; + + int num = hello->getNumSas(); + if (num == 0) { + return &zrtpSasTypes.getByName(mandatorySasType); + } + // Buildlist of configured SAS algorithm names, append mandatory algos + // if necessary. + numAlgosConf = configureAlgos.getNumConfiguredAlgos(SasType); + for (i = 0; i < numAlgosConf; i++) { + algosConf[i] = &configureAlgos.getAlgoAt(SasType, i); + if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatorySasType) { + mandatoryFound = true; + } + } + + if (!mandatoryFound) { + algosConf[numAlgosConf++] = &zrtpSasTypes.getByName(mandatorySasType); + } + + // Build list of offered known algos in Hello, append mandatory algos if necessary + for (numAlgosOffered = 0, i = 0; i < num; i++) { + algosOffered[numAlgosOffered] = &zrtpSasTypes.getByName((const char*)hello->getSasType(i)); + if (!algosOffered[numAlgosOffered]->isValid()) + continue; + if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatorySasType) { + mandatoryFound = true; + } + } + + if (!mandatoryFound) { + algosOffered[numAlgosOffered++] = &zrtpSasTypes.getByName(mandatorySasType); + } + + // Lookup offered algos in configured algos. Because of appended + // mandatory algorithms at least one match will happen + for (i = 0; i < numAlgosOffered; i++) { + for (ii = 0; ii < numAlgosConf; ii++) { + if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) { + return algosConf[ii]; + } + } + } + return &zrtpSasTypes.getByName(mandatorySasType); +} + +AlgorithmEnum* ZRtp::findBestAuthLen(ZrtpPacketHello *hello) { + + int i; + int ii; + int numAlgosOffered; + AlgorithmEnum* algosOffered[ZrtpConfigure::maxNoOfAlgos+2]; + + int numAlgosConf; + AlgorithmEnum* algosConf[ZrtpConfigure::maxNoOfAlgos+2]; + + bool mandatoryFound_1 = false; + bool mandatoryFound_2 = false; + + int num = hello->getNumAuth(); + if (num == 0) { + return &zrtpAuthLengths.getByName(mandatoryAuthLen_1); + } + + // Build list of configured SAS algorithm names, append mandatory algos + // if necessary. + numAlgosConf = configureAlgos.getNumConfiguredAlgos(AuthLength); + for (i = 0; i < numAlgosConf; i++) { + algosConf[i] = &configureAlgos.getAlgoAt(AuthLength, i); + if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryAuthLen_1) { + mandatoryFound_1 = true; + } + if (*(int32_t*)(algosConf[i]->getName()) == *(int32_t*)mandatoryAuthLen_2) { + mandatoryFound_2 = true; + } + } + + if (!mandatoryFound_1) { + algosConf[numAlgosConf++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_1); + } + + if (!mandatoryFound_2) { + algosConf[numAlgosConf++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_2); + } + + // Build list of offered known algos in Hello, append mandatory algos if necessary + for (numAlgosOffered = 0, i = 0; i < num; i++) { + algosOffered[numAlgosOffered] = &zrtpAuthLengths.getByName((const char*)hello->getAuthLen(i)); + if (!algosOffered[numAlgosOffered]->isValid()) + continue; + if (*(int32_t*)(algosOffered[numAlgosOffered]->getName()) == *(int32_t*)mandatoryAuthLen_1) { + mandatoryFound_1 = true; + } + if (*(int32_t*)(algosOffered[numAlgosOffered++]->getName()) == *(int32_t*)mandatoryAuthLen_2) { + mandatoryFound_2 = true; + } + } + if (!mandatoryFound_1) { + algosOffered[numAlgosOffered++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_1); + } + if (!mandatoryFound_2) { + algosOffered[numAlgosOffered++] = &zrtpAuthLengths.getByName(mandatoryAuthLen_2); + } + // Lookup offered algos in configured algos. Because of appended + // mandatory algorithms at least one match will happen + for (i = 0; i < numAlgosOffered; i++) { + for (ii = 0; ii < numAlgosConf; ii++) { + if (*(int32_t*)(algosOffered[i]->getName()) == *(int32_t*)(algosConf[ii]->getName())) { + return algosConf[ii]; + } + } + } + return &zrtpAuthLengths.getByName(mandatoryAuthLen_1); +} + +bool ZRtp::checkMultiStream(ZrtpPacketHello *hello) { + + int i; + int num = hello->getNumPubKeys(); + + // Multi Stream mode is mandatory, thus if nothing is offered then it is supported :-) + if (num == 0) { + return true; + } + for (i = 0; i < num; i++) { + if (*(int32_t*)(hello->getPubKeyType(i)) == *(int32_t*)mult) { + return true; + } + } + return false; +} + +bool ZRtp::verifyH2(ZrtpPacketCommit *commit) { + uint8_t tmpH3[IMPL_MAX_DIGEST_LENGTH]; + + sha256(commit->getH2(), HASH_IMAGE_SIZE, tmpH3); + if (memcmp(tmpH3, peerH3, HASH_IMAGE_SIZE) != 0) { + return false; + } + return true; +} + +void ZRtp::computeHvi(ZrtpPacketDHPart* dh, ZrtpPacketHello *hello) { + + unsigned char* data[3]; + unsigned int length[3]; + /* + * populate the vector to compute the HVI hash according to the + * ZRTP specification. + */ + data[0] = (uint8_t*)dh->getHeaderBase(); + length[0] = dh->getLength() * ZRTP_WORD_SIZE; + + data[1] = (uint8_t*)hello->getHeaderBase(); + length[1] = hello->getLength() * ZRTP_WORD_SIZE; + + data[2] = NULL; // terminate data chunks + hashListFunction(data, length, hvi); + return; +} + +void ZRtp:: computeSharedSecretSet(ZIDRecord &zidRec) { + + /* + * Compute the Initiator's and Reponder's retained shared secret Ids. + * Use negotiated HMAC. + */ + uint8_t randBuf[RS_LENGTH]; + uint32_t macLen; + + if (!zidRec.isRs1Valid()) { + randomZRTP(randBuf, RS_LENGTH); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs1IDi, &macLen); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), rs1IDr, &macLen); + } + else { + rs1Valid = true; + hmacFunction((unsigned char*)zidRec.getRs1(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs1IDi, &macLen); + hmacFunction((unsigned char*)zidRec.getRs1(), RS_LENGTH, (unsigned char*)responder, strlen(responder), rs1IDr, &macLen); + } + + if (!zidRec.isRs2Valid()) { + randomZRTP(randBuf, RS_LENGTH); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs2IDi, &macLen); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), rs2IDr, &macLen); + } + else { + rs2Valid = true; + hmacFunction((unsigned char*)zidRec.getRs2(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), rs2IDi, &macLen); + hmacFunction((unsigned char*)zidRec.getRs2(), RS_LENGTH, (unsigned char*)responder, strlen(responder), rs2IDr, &macLen); + } + + /* + * For the time being we don't support this types of shared secrect. Could be + * easily done: somebody sets some data into our ZRtp object, check it here + * and use it. Otherwise use the random data. + */ + randomZRTP(randBuf, RS_LENGTH); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), auxSecretIDi, &macLen); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), auxSecretIDr, &macLen); + + if (!zidRec.isMITMKeyAvailable()) { + randomZRTP(randBuf, RS_LENGTH); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)initiator, strlen(initiator), pbxSecretIDi, &macLen); + hmacFunction(randBuf, RS_LENGTH, (unsigned char*)responder, strlen(responder), pbxSecretIDr, &macLen); + } + else { + hmacFunction((unsigned char*)zidRec.getMiTMData(), RS_LENGTH, (unsigned char*)initiator, strlen(initiator), pbxSecretIDi, &macLen); + hmacFunction((unsigned char*)zidRec.getMiTMData(), RS_LENGTH, (unsigned char*)responder, strlen(responder), pbxSecretIDr, &macLen); + } +} + +/* + * The DH packet for this function is DHPart1 and contains the Responder's + * retained secret ids. Compare them with the expected secret ids (refer + * to chapter 5.3 in the specification). + * When using this method then we are in Initiator role. + */ +void ZRtp::generateKeysInitiator(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec) { + const uint8_t* setD[3]; + int32_t rsFound = 0; + + setD[0] = setD[1] = setD[2] = NULL; + + /* + * Select the real secrets into setD. The dhPart is DHpart1 message + * received from responder. rs1IDr and rs2IDr are the expected ids using + * the initator's cached retained secrets. + */ + int matchingSecrets = 0; + if (memcmp(rs1IDr, dhPart->getRs1Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs1(); + rsFound = 0x1; + } + else if (memcmp(rs1IDr, dhPart->getRs2Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs1(); + rsFound = 0x2; + } + else if (memcmp(rs2IDr, dhPart->getRs1Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs2(); + rsFound = 0x4; + } + else if (memcmp(rs2IDr, dhPart->getRs2Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs2(); + rsFound = 0x8; + } + /* *** Not yet supported + if (memcmp(auxSecretIDr, dhPart->getAuxSecretId(), 8) == 0) { + DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", zid[0]))); + setD[matchingSecrets++] = auxSecret; + } + */ + if (memcmp(pbxSecretIDr, dhPart->getPbxSecretId(), 8) == 0) { + DEBUGOUT((fprintf(stdout, "%c: Match for Other_secret found\n", zid[0]))); + setD[matchingSecrets++] = zidRec.getMiTMData(); + } + // Check if some retained secrets found + if (rsFound == 0) { // no RS matches found + if (rs1Valid || rs2Valid) { // but valid RS records in cache + sendInfo(Warning, WarningNoExpectedRSMatch); + zidRec.resetSasVerified(); + } + else { // No valid RS record in cache + sendInfo(Warning, WarningNoRSMatch); + } + } + else { // at least one RS matches + sendInfo(Info, InfoRSMatchFound); + } + /* + * Ready to generate s0 here. + * The formular to compute S0 (Refer to ZRTP specification 5.4.4): + * + s0 = hash( counter | DHResult | "ZRTP-HMAC-KDF" | ZIDi | ZIDr | \ + total_hash | len(s1) | s1 | len(s2) | s2 | len(s3) | s3) + * + * Note: in this function we are Initiator, thus ZIDi is our zid + * (zid), ZIDr is the peer's zid (peerZid). + */ + + /* + * These arrays hold the pointers and lengths of the data that must be + * hashed to create S0. According to the formula the max number of + * elements to hash is 12, add one for the terminating "NULL" + */ + unsigned char* data[13]; + unsigned int length[13]; + uint32_t pos = 0; // index into the array + + // we need a number of length data items, so define them here + uint32_t counter, sLen[3]; + + //Very first element is a fixed counter, big endian + counter = 1; + counter = htonl(counter); + data[pos] = (unsigned char*)&counter; + length[pos++] = sizeof(uint32_t); + + // Next is the DH result itself + data[pos] = DHss; + length[pos++] = dhContext->getDhSize(); + + // Next the fixed string "ZRTP-HMAC-KDF" + data[pos] = (unsigned char*)KDFString; + length[pos++] = strlen(KDFString); + + // Next is Initiator's id (ZIDi), in this case as Initiator + // it is zid + data[pos] = zid; + length[pos++] = ZID_SIZE; + + // Next is Responder's id (ZIDr), in this case our peer's id + data[pos] = peerZid; + length[pos++] = ZID_SIZE; + + // Next ist total hash (messageHash) itself + data[pos] = messageHash; + length[pos++] = hashLength; + + /* + * For each matching shared secret hash the length of + * the shared secret as 32 bit big-endian number followd by the + * shared secret itself. The length of a shared seceret is + * currently fixed to RS_LENGTH. If a shared + * secret is not used _only_ its length is hased as zero + * length. NOTE: if implementing auxSecret and/or pbxSecret -> check + * this length stuff again. + */ + int secretHashLen = RS_LENGTH; + secretHashLen = htonl(secretHashLen); // prepare 32 bit big-endian number + + for (int32_t i = 0; i < 3; i++) { + if (setD[i] != NULL) { // a matching secret, set length, then secret + sLen[i] = secretHashLen; + data[pos] = (unsigned char*)&sLen[i]; + length[pos++] = sizeof(uint32_t); + data[pos] = (unsigned char*)setD[i]; + length[pos++] = RS_LENGTH; + } + else { // no machting secret, set length 0, skip secret + sLen[i] = 0; + data[pos] = (unsigned char*)&sLen[i]; + length[pos++] = sizeof(uint32_t); + } + } + + data[pos] = NULL; + hashListFunction(data, length, s0); +// hexdump("S0 I", s0, hashLength); + + memset(DHss, 0, dhContext->getDhSize()); + delete[] DHss; + DHss = NULL; + + computeSRTPKeys(); + memset(s0, 0, MAX_DIGEST_LENGTH); +} +/* + * The DH packet for this function is DHPart2 and contains the Initiator's + * retained secret ids. Compare them with the expected secret ids (refer + * to chapter 5.3.1 in the specification). + */ +void ZRtp::generateKeysResponder(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec) { + const uint8_t* setD[3]; + int32_t rsFound = 0; + + setD[0] = setD[1] = setD[2] = NULL; + + /* + * Select the real secrets into setD + */ + int matchingSecrets = 0; + if (memcmp(rs1IDi, dhPart->getRs1Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs1(); + rsFound = 0x1; + } + else if (memcmp(rs1IDi, dhPart->getRs2Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs1(); + rsFound = 0x2; + } + else if (memcmp(rs2IDi, dhPart->getRs2Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs2(); + rsFound |= 0x4; + } + else if (memcmp(rs2IDi, dhPart->getRs1Id(), HMAC_SIZE) == 0) { + setD[matchingSecrets++] = zidRec.getRs2(); + rsFound |= 0x8; + } + /* ***** not yet supported + if (memcmp(auxSecretIDi, dhPart->getauxSecretId(), 8) == 0) { + DEBUGOUT((fprintf(stdout, "%c: Match for aux secret found\n", zid[0]))); + setD[matchingSecrets++] = ; + } + */ + if (memcmp(pbxSecretIDi, dhPart->getPbxSecretId(), 8) == 0) { + DEBUGOUT((fprintf(stdout, "%c: Match for PBX secret found\n", zid[0]))); + setD[matchingSecrets++] = zidRec.getMiTMData(); + } + // Check if some retained secrets found + if (rsFound == 0) { // no RS matches found + if (rs1Valid || rs2Valid) { // but valid RS records in cache + sendInfo(Warning, WarningNoExpectedRSMatch); + zidRec.resetSasVerified(); + } + else { // No valid RS record in cache + sendInfo(Warning, WarningNoRSMatch); + } + } + else { // at least one RS matches + sendInfo(Info, InfoRSMatchFound); + } + + /* + * ready to generate s0 here. + * The formular to compute S0 (Refer to ZRTP specification 5.4.4): + * + s0 = hash( counter | DHResult | "ZRTP-HMAC-KDF" | ZIDi | ZIDr | \ + total_hash | len(s1) | s1 | len(s2) | s2 | len(s3) | s3) + * + * Note: in this function we are Responder, thus ZIDi is the peer's zid + * (peerZid), ZIDr is our zid. + */ + + /* + * These arrays hold the pointers and lengths of the data that must be + * hashed to create S0. According to the formula the max number of + * elements to hash is 12, add one for the terminating "NULL" + */ + unsigned char* data[13]; + unsigned int length[13]; + uint32_t pos = 0; // index into the array + + + // we need a number of length data items, so define them here + uint32_t counter, sLen[3]; + + //Very first element is a fixed counter, big endian + counter = 1; + counter = htonl(counter); + data[pos] = (unsigned char*)&counter; + length[pos++] = sizeof(uint32_t); + + // Next is the DH result itself + data[pos] = DHss; + length[pos++] = dhContext->getDhSize(); + + // Next the fixed string "ZRTP-HMAC-KDF" + data[pos] = (unsigned char*)KDFString; + length[pos++] = strlen(KDFString); + + // Next is Initiator's id (ZIDi), in this case as Responder + // it is peerZid + data[pos] = peerZid; + length[pos++] = ZID_SIZE; + + // Next is Responder's id (ZIDr), in this case our own zid + data[pos] = zid; + length[pos++] = ZID_SIZE; + + // Next ist total hash (messageHash) itself + data[pos] = messageHash; + length[pos++] = hashLength; + + /* + * For each matching shared secret hash the length of + * the shared secret as 32 bit big-endian number followd by the + * shared secret itself. The length of a shared seceret is + * currently fixed to SHA256_DIGEST_LENGTH. If a shared + * secret is not used _only_ its length is hased as zero + * length. NOTE: if implementing auxSecret and/or pbxSecret -> check + * this length stuff again. + */ + int secretHashLen = RS_LENGTH; + secretHashLen = htonl(secretHashLen); // prepare 32 bit big-endian number + + for (int32_t i = 0; i < 3; i++) { + if (setD[i] != NULL) { // a matching secret, set length, then secret + sLen[i] = secretHashLen; + data[pos] = (unsigned char*)&sLen[i]; + length[pos++] = sizeof(uint32_t); + data[pos] = (unsigned char*)setD[i]; + length[pos++] = RS_LENGTH; + } + else { // no machting secret, set length 0, skip secret + sLen[i] = 0; + data[pos] = (unsigned char*)&sLen[i]; + length[pos++] = sizeof(uint32_t); + } + } + + data[pos] = NULL; + hashListFunction(data, length, s0); +// hexdump("S0 R", s0, hashLength); + + memset(DHss, 0, dhContext->getDhSize()); + delete[] DHss; + DHss = NULL; + + computeSRTPKeys(); + memset(s0, 0, MAX_DIGEST_LENGTH); +} + + +void ZRtp::KDF(uint8_t* key, uint32_t keyLength, uint8_t* label, int32_t labelLength, + uint8_t* context, int32_t contextLength, int32_t L, uint8_t* output) { + + unsigned char* data[6]; + uint32_t length[6]; + uint32_t pos = 0; // index into the array + uint32_t maclen = 0; + + // Very first element is a fixed counter, big endian + uint32_t counter = 1; + counter = htonl(counter); + data[pos] = (unsigned char*)&counter; + length[pos++] = sizeof(uint32_t); + + // Next element is the label, null terminated, labelLength includes null byte. + data[pos] = label; + length[pos++] = labelLength; + + // Next is the KDF context + data[pos] = context; + length[pos++] = contextLength; + + // last element is HMAC length in bits, big endian + uint32_t len = htonl(L); + data[pos] = (unsigned char*)&len; + length[pos++] = sizeof(uint32_t); + + data[pos] = NULL; + + // Use negotiated hash. + hmacListFunction(key, keyLength, data, length, output, &maclen); +} + +// Compute the Multi Stream mode s0 +void ZRtp::generateKeysMultiStream() { + + // allocate the maximum size, compute real size to use + uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)+sizeof(messageHash)]; + int32_t kdfSize = sizeof(peerZid)+sizeof(zid)+hashLength; + + if (myRole == Responder) { + memcpy(KDFcontext, peerZid, sizeof(peerZid)); + memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid)); + } + else { + memcpy(KDFcontext, zid, sizeof(zid)); + memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid)); + } + memcpy(KDFcontext+sizeof(zid)+sizeof(peerZid), messageHash, hashLength); + + KDF(zrtpSession, hashLength, (unsigned char*)zrtpMsk, strlen(zrtpMsk)+1, KDFcontext, kdfSize, hashLength*8, s0); + + memset(KDFcontext, 0, sizeof(KDFcontext)); + + computeSRTPKeys(); +} + +void ZRtp::computePBXSecret() { + // Construct the KDF context as per ZRTP specification chap 7.3.1: + // ZIDi || ZIDr + uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)]; + int32_t kdfSize = sizeof(peerZid)+sizeof(zid); + + if (myRole == Responder) { + memcpy(KDFcontext, peerZid, sizeof(peerZid)); + memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid)); + } + else { + memcpy(KDFcontext, zid, sizeof(zid)); + memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid)); + } + + KDF(zrtpSession, hashLength, (unsigned char*)zrtpTrustedMitm, strlen(zrtpTrustedMitm)+1, KDFcontext, + kdfSize, SHA256_DIGEST_LENGTH * 8, pbxSecretTmpBuffer); + + pbxSecretTmp = pbxSecretTmpBuffer; // set pointer to buffer, signal PBX secret was computed +} + + +void ZRtp::computeSRTPKeys() { + + // allocate the maximum size, compute real size to use + uint8_t KDFcontext[sizeof(peerZid)+sizeof(zid)+sizeof(messageHash)]; + int32_t kdfSize = sizeof(peerZid)+sizeof(zid)+hashLength; + + int32_t keyLen = cipher->getKeylen() * 8; + + if (myRole == Responder) { + memcpy(KDFcontext, peerZid, sizeof(peerZid)); + memcpy(KDFcontext+sizeof(peerZid), zid, sizeof(zid)); + } + else { + memcpy(KDFcontext, zid, sizeof(zid)); + memcpy(KDFcontext+sizeof(zid), peerZid, sizeof(peerZid)); + } + memcpy(KDFcontext+sizeof(zid)+sizeof(peerZid), messageHash, hashLength); + + // Inititiator key and salt + KDF(s0, hashLength, (unsigned char*)iniMasterKey, strlen(iniMasterKey)+1, KDFcontext, kdfSize, keyLen, srtpKeyI); + KDF(s0, hashLength, (unsigned char*)iniMasterSalt, strlen(iniMasterSalt)+1, KDFcontext, kdfSize, 112, srtpSaltI); + + // Responder key and salt + KDF(s0, hashLength, (unsigned char*)respMasterKey, strlen(respMasterKey)+1, KDFcontext, kdfSize, keyLen, srtpKeyR); + KDF(s0, hashLength, (unsigned char*)respMasterSalt, strlen(respMasterSalt)+1, KDFcontext, kdfSize, 112, srtpSaltR); + + // The HMAC keys for GoClear + KDF(s0, hashLength, (unsigned char*)iniHmacKey, strlen(iniHmacKey)+1, KDFcontext, kdfSize, hashLength*8, hmacKeyI); + + KDF(s0, hashLength, (unsigned char*)respHmacKey, strlen(respHmacKey)+1, KDFcontext, kdfSize, hashLength*8, hmacKeyR); + + // The keys for Confirm messages + KDF(s0, hashLength, (unsigned char*)iniZrtpKey, strlen(iniZrtpKey)+1, KDFcontext, kdfSize, keyLen, zrtpKeyI); + KDF(s0, hashLength, (unsigned char*)respZrtpKey, strlen(respZrtpKey)+1, KDFcontext, kdfSize, keyLen, zrtpKeyR); + + if (!multiStream) { + // Compute the new Retained Secret + KDF(s0, hashLength, (unsigned char*)retainedSec, strlen(retainedSec)+1, KDFcontext, kdfSize, SHA256_DIGEST_LENGTH*8, newRs1); + + // Compute the ZRTP Session Key + KDF(s0, hashLength, (unsigned char*)zrtpSessionKey, strlen(zrtpSessionKey)+1, KDFcontext, kdfSize, hashLength*8, zrtpSession); + + // perform SAS generation according to chapter 5.5 and 8. + // we don't need a speciai sasValue filed. sasValue are the first + // (leftmost) 32 bits (4 bytes) of sasHash + uint8_t sasBytes[4]; + KDF(s0, hashLength, (unsigned char*)sasString, strlen(sasString)+1, KDFcontext, kdfSize, SHA256_DIGEST_LENGTH*8, sasHash); + + // according to chapter 8 only the leftmost 20 bits of sasValue (aka + // sasHash) are used to create the character SAS string of type SAS + // base 32 (5 bits per character) + sasBytes[0] = sasHash[0]; + sasBytes[1] = sasHash[1]; + sasBytes[2] = sasHash[2] & 0xf0; + sasBytes[3] = 0; + SAS = Base32(sasBytes, 20).getEncoded(); + if (signSasSeen) + callback->signSAS(sasHash); + } + memset(KDFcontext, 0, sizeof(KDFcontext)); +} + +bool ZRtp::srtpSecretsReady(EnableSecurity part) { + + SrtpSecret_t sec; + + sec.symEncAlgorithm = cipher->getAlgoId(); + + sec.keyInitiator = srtpKeyI; + sec.initKeyLen = cipher->getKeylen() * 8; + sec.saltInitiator = srtpSaltI; + sec.initSaltLen = 112; + + sec.keyResponder = srtpKeyR; + sec.respKeyLen = cipher->getKeylen() * 8; + sec.saltResponder = srtpSaltR; + sec.respSaltLen = 112; + + sec.authAlgorithm = authLength->getAlgoId(); + sec.srtpAuthTagLen = authLength->getKeylen(); + + sec.sas = SAS; + sec.role = myRole; + + return callback->srtpSecretsReady(&sec, part); +} + + +void ZRtp::setNegotiatedHash(AlgorithmEnum* hash) { + switch (zrtpHashes.getOrdinal(*hash)) { + case 0: + hashLength = SHA256_DIGEST_LENGTH; + hashFunction = sha256; + hashListFunction = sha256; + + hmacFunction = hmac_sha256; + hmacListFunction = hmac_sha256; + + createHashCtx = createSha256Context; + closeHashCtx = closeSha256Context; + hashCtxFunction = sha256Ctx; + hashCtxListFunction = sha256Ctx; + break; + + case 1: + hashLength = SHA384_DIGEST_LENGTH; + hashFunction = sha384; + hashListFunction = sha384; + + hmacFunction = hmac_sha384; + hmacListFunction = hmac_sha384; + + createHashCtx = createSha384Context; + closeHashCtx = closeSha384Context; + hashCtxFunction = sha384Ctx; + hashCtxListFunction = sha384Ctx; + break; + } +} + + +void ZRtp::srtpSecretsOff(EnableSecurity part) { + callback->srtpSecretsOff(part); +} + +void ZRtp::SASVerified() { + if (paranoidMode) + return; + + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + ZIDFile *zid = ZIDFile::getInstance(); + + zid->getRecord(&zidRec); + zidRec.setSasVerified(); + zid->saveRecord(&zidRec); +} + +void ZRtp::resetSASVerified() { + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + ZIDFile *zid = ZIDFile::getInstance(); + + zid->getRecord(&zidRec); + zidRec.resetSasVerified(); + zid->saveRecord(&zidRec); +} + + +void ZRtp::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) { + + // We've reached secure state: overwrite the SRTP master key and master salt. + if (severity == Info && subCode == InfoSecureStateOn) { + memset(srtpKeyI, 0, cipher->getKeylen()); + memset(srtpSaltI, 0, 112/8); + memset(srtpKeyR, 0, cipher->getKeylen()); + memset(srtpSaltR, 0, 112/8); + } + callback->sendInfo(severity, subCode); +} + + +void ZRtp::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) { + callback->zrtpNegotiationFailed(severity, subCode); +} + +void ZRtp::zrtpNotSuppOther() { + callback->zrtpNotSuppOther(); +} + +void ZRtp::synchEnter() { + callback->synchEnter(); +} + +void ZRtp::synchLeave() { + callback->synchLeave(); +} + +int32_t ZRtp::sendPacketZRTP(ZrtpPacketBase *packet) { + return ((packet == NULL) ? 0 : + callback->sendDataZRTP(packet->getHeaderBase(), (packet->getLength() * 4) + 4)); +} + +int32_t ZRtp::activateTimer(int32_t tm) { + return (callback->activateTimer(tm)); +} + +int32_t ZRtp::cancelTimer() { + return (callback->cancelTimer()); +} + +void ZRtp::setAuxSecret(uint8_t* data, int32_t length) { + if (length > 0) { + auxSecret = new uint8_t[length]; + auxSecretLength = length; + memcpy(auxSecret, data, length); + } +} + +void ZRtp::setClientId(std::string id) { + if (id.size() < CLIENT_ID_SIZE) { + unsigned char tmp[CLIENT_ID_SIZE +1] = {' '}; + memcpy(tmp, id.c_str(), id.size()); + tmp[CLIENT_ID_SIZE] = 0; + zrtpHello.setClientId(tmp); + } else { + zrtpHello.setClientId((unsigned char*)id.c_str()); + } + + int32_t len = zrtpHello.getLength() * ZRTP_WORD_SIZE; + + // Hello packet is ready now, compute its HMAC + // (excluding the HMAC field (2*ZTP_WORD_SIZE)) and store in Hello + // use the implicit hash function + uint8_t hmac[IMPL_MAX_DIGEST_LENGTH]; + uint32_t macLen; + hmacFunctionImpl(H2, HASH_IMAGE_SIZE, (uint8_t*)zrtpHello.getHeaderBase(), len-(2*ZRTP_WORD_SIZE), hmac, &macLen); + zrtpHello.setHMAC(hmac); + + // calculate hash over the final Hello packet, refer to chap 9.1 how to + // use this hash in SIP/SDP. + hashFunctionImpl((uint8_t*)zrtpHello.getHeaderBase(), len, helloHash); +} + +void ZRtp::storeMsgTemp(ZrtpPacketBase* pkt) { + uint32_t length = pkt->getLength() * ZRTP_WORD_SIZE; + length = (length > sizeof(tempMsgBuffer)) ? sizeof(tempMsgBuffer) : length; + memset(tempMsgBuffer, 0, sizeof(tempMsgBuffer)); + memcpy(tempMsgBuffer, (uint8_t*)pkt->getHeaderBase(), length); + lengthOfMsgData = length; +} + +bool ZRtp::checkMsgHmac(uint8_t* key) { + uint8_t hmac[IMPL_MAX_DIGEST_LENGTH]; + uint32_t macLen; + int32_t len = lengthOfMsgData-(HMAC_SIZE); // compute HMAC, but exlude the stored HMAC :-) + + // Use the implicit hash function + hmacFunctionImpl(key, HASH_IMAGE_SIZE, tempMsgBuffer, len, hmac, &macLen); + return (memcmp(hmac, tempMsgBuffer+len, (HMAC_SIZE)) == 0 ? true : false); +} + +std::string ZRtp::getHelloHash() { + std::ostringstream stm; + + uint8_t* hp = helloHash; + + stm << zrtpVersion; + stm << " "; + stm.fill('0'); + stm << hex; + for (int i = 0; i < hashLengthImpl; i++) { + stm.width(2); + stm << static_cast<uint32_t>(*hp++); + } + return stm.str(); +} + +std::string ZRtp::getPeerHelloHash() { + std::ostringstream stm; + + if (peerHelloVersion[0] == 0) + return std::string(); + + uint8_t* hp = peerHelloHash; + + stm << peerHelloVersion; + stm << " "; + stm.fill('0'); + stm << hex; + for (int i = 0; i < hashLengthImpl; i++) { + stm.width(2); + stm << static_cast<uint32_t>(*hp++); + } + return stm.str(); +} + +std::string ZRtp::getMultiStrParams() { + + // the string will hold binary data - it's opaque to the application + std::string str(""); + char tmp[MAX_DIGEST_LENGTH + 1 + 1 + 1]; // hash length + cipher + authLength + hash + + if (inState(SecureState) && !multiStream) { + // construct array that holds zrtpSession, cipher type, auth-length, and hash type + tmp[0] = zrtpHashes.getOrdinal(*hash); + tmp[1] = zrtpAuthLengths.getOrdinal(*authLength); + tmp[2] = zrtpSymCiphers.getOrdinal(*cipher); + memcpy(tmp+3, zrtpSession, hashLength); + str.assign(tmp, hashLength + 1 + 1 + 1); // set chars (bytes) to the string + } + return str; +} + +void ZRtp::setMultiStrParams(std::string parameters) { + + char tmp[MAX_DIGEST_LENGTH + 1 + 1 + 1]; // max. hash length + cipher + authLength + hash + + // First get negotiated hash from parameters, set algorithms and length + int i = parameters.at(0) & 0xff; + hash = &zrtpHashes.getByOrdinal(i); + setNegotiatedHash(hash); // sets hashlength + + // use string.copy(buffer, num, start=0) to retrieve chars (bytes) from the string + parameters.copy(tmp, hashLength + 1 + 1 + 1, 0); + + i = tmp[1] & 0xff; + authLength = &zrtpAuthLengths.getByOrdinal(i); + i = tmp[2] & 0xff; + cipher = &zrtpSymCiphers.getByOrdinal(i); + memcpy(zrtpSession, tmp+3, hashLength); + + // after setting zrtpSession, cipher, and auth-length set multi-stream to true + multiStream = true; + stateEngine->setMultiStream(true); +} + +bool ZRtp::isMultiStream() { + return multiStream; +} + +bool ZRtp::isMultiStreamAvailable() { + return multiStreamAvailable; +} + +void ZRtp::acceptEnrollment(bool accepted) { + if (!accepted) { + callback->zrtpInformEnrollment(EnrollmentCanceled); + return; + } + // Get peer's zid record to store the pbx (MitM) secret + // Initialize a ZID record to get peer's retained secrets + ZIDRecord zidRec(peerZid); + ZIDFile* zid = ZIDFile::getInstance(); + zid->getRecord(&zidRec); + + if (pbxSecretTmp != NULL) { + zidRec.setMiTMData(pbxSecretTmp); + callback->zrtpInformEnrollment(EnrollmentOk); + } + else { + callback->zrtpInformEnrollment(EnrollmentFailed); + return; + } + zid->saveRecord(&zidRec); + return; +} + +bool ZRtp::setSignatureData(uint8_t* data, int32_t length) { + if ((length % 4) != 0) + return false; + + ZrtpPacketConfirm* cfrm = (myRole == Responder) ? &zrtpConfirm1 : &zrtpConfirm2; + cfrm->setSignatureLength(length / 4); + return cfrm->setSignatureData(data, length); +} + +const uint8_t* ZRtp::getSignatureData() { + return signatureData; +} + +int32_t ZRtp::getSignatureLength() { + return signatureLength * ZRTP_WORD_SIZE; +} + +void ZRtp::conf2AckSecure() { + Event_t ev; + + ev.type = ZrtpPacket; + ev.packet = (uint8_t*)&zrtpConf2Ack; + + if (stateEngine != NULL) { + stateEngine->processEvent(&ev); + } +} + +int32_t ZRtp::compareCommit(ZrtpPacketCommit *commit) { + // TODO: enhance to compare according to rules defined in chapter 4.2 + int32_t len = 0; + len = !multiStream ? HVI_SIZE : (4 * ZRTP_WORD_SIZE); + return (memcmp(hvi, commit->getHvi(), len)); +} + +bool ZRtp::isEnrollmentMode() { + return enrollmentMode; +} + +void ZRtp::setEnrollmentMode(bool enrollmentMode) { + this->enrollmentMode = enrollmentMode; +} + +bool ZRtp::isPeerEnrolled() { + return peerIsEnrolled; +} + +bool ZRtp::sendSASRelayPacket(uint8_t* sh, std::string render) { + + uint8_t confMac[MAX_DIGEST_LENGTH]; + uint32_t macLen; + uint8_t* hkey, *ekey; + + // If we are responder then the PBX used it's Initiator keys + if (myRole == Responder) { + hkey = hmacKeyR; + ekey = zrtpKeyR; + // TODO: check signature length in zrtpConfirm1 and if not zero copy Signature data + } + else { + hkey = hmacKeyI; + ekey = zrtpKeyI; + // TODO: check signature length in zrtpConfirm2 and if not zero copy Signature data + } + // Prepare IV data that we will use during confirm packet encryption. + randomZRTP(randomIV, sizeof(randomIV)); + zrtpSasRelay.setIv(randomIV); + zrtpSasRelay.setTrustedSas(sh); + zrtpSasRelay.setSas((uint8_t*)render.c_str()); + + // Encrypt and HMAC with Initiator's key - we are Initiator here + int16_t hmlen = (zrtpSasRelay.getLength() - 9) * ZRTP_WORD_SIZE; + cipher->getEncrypt()(ekey, cipher->getKeylen(), randomIV, (uint8_t*)zrtpSasRelay.getFiller(), hmlen); + + // Use negotiated HMAC (hash) + hmacFunction(hkey, hashLength, (unsigned char*)zrtpSasRelay.getFiller(), hmlen, confMac, &macLen); + + zrtpSasRelay.setHmac(confMac); + + stateEngine->sendSASRelay(&zrtpSasRelay); + return true; +} + +std::string ZRtp::getSasType() { + std::string sasT(sasType->getName()); + return sasT; +} + +uint8_t* ZRtp::getSasHash() { + return sasHash; +} + +int32_t ZRtp::getPeerZid(uint8_t* data) { + memcpy(data, peerZid, IDENTIFIER_LEN); + return IDENTIFIER_LEN; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/src/ZrtpCWrapper.cpp b/src/ZrtpCWrapper.cpp new file mode 100644 index 0000000..1e1d2c8 --- /dev/null +++ b/src/ZrtpCWrapper.cpp @@ -0,0 +1,470 @@ +/* + This class maps the ZRTP C calls to ZRTP C++ methods. + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#include <libzrtpcpp/ZrtpCallback.h> +#include <libzrtpcpp/ZrtpConfigure.h> +#include <libzrtpcpp/ZIDFile.h> +#include <libzrtpcpp/ZRtp.h> +#include <libzrtpcpp/ZrtpCallbackWrapper.h> +#include <libzrtpcpp/ZrtpCWrapper.h> +#include <libzrtpcpp/ZrtpCrc32.h> + +static int32_t zrtp_initZidFile(const char* zidFilename); + +ZrtpContext* zrtp_CreateWrapper() +{ + ZrtpContext* zc = new ZrtpContext; + zc->configure = 0; + zc->zrtpEngine = 0; + zc->zrtpCallback = 0; + + return zc; +} + +void zrtp_initializeZrtpEngine(ZrtpContext* zrtpContext, + zrtp_Callbacks *cb, const char* id, + const char* zidFilename, + void* userData, + int32_t mitmMode) +{ + std::string clientIdString(id); + + zrtpContext->zrtpCallback = new ZrtpCallbackWrapper(cb, zrtpContext); + zrtpContext->userData = userData; + + if (zrtpContext->configure == 0) { + zrtpContext->configure = new ZrtpConfigure(); + zrtpContext->configure->setStandardConfig(); + } + + // Initialize ZID file (cache) and get my own ZID + zrtp_initZidFile(zidFilename); + ZIDFile* zf = ZIDFile::getInstance(); + const unsigned char* myZid = zf->getZid(); + + zrtpContext->zrtpEngine = new ZRtp((uint8_t*)myZid, zrtpContext->zrtpCallback, + clientIdString, zrtpContext->configure, mitmMode == 0 ? false : true); +} + +void zrtp_DestroyWrapper(ZrtpContext* zrtpContext) { + + if (zrtpContext == NULL) + return; + + delete zrtpContext->zrtpEngine; + zrtpContext->zrtpEngine = NULL; + + delete zrtpContext->zrtpCallback; + zrtpContext->zrtpCallback = NULL; + + delete zrtpContext->configure; + zrtpContext->configure = NULL; + + delete zrtpContext; +} + +static int32_t zrtp_initZidFile(const char* zidFilename) { + ZIDFile* zf = ZIDFile::getInstance(); + + if (!zf->isOpen()) { + std::string fname; + if (zidFilename == NULL) { + char *home = getenv("HOME"); + std::string baseDir = (home != NULL) ? (std::string(home) + std::string("/.")) + : std::string("."); + fname = baseDir + std::string("GNUccRTP.zid"); + zidFilename = fname.c_str(); + } + return zf->open((char *)zidFilename); + } + return 0; +} + +int32_t zrtp_CheckCksum(uint8_t* buffer, uint16_t temp, uint32_t crc) +{ + return zrtpCheckCksum(buffer, temp, crc); +} + +uint32_t zrtp_GenerateCksum(uint8_t* buffer, uint16_t temp) +{ + return zrtpGenerateCksum(buffer, temp); +} + +uint32_t zrtp_EndCksum(uint32_t crc) +{ + return zrtpEndCksum(crc); +} + +/* + * Applications use the following methods to control ZRTP, for example + * to enable ZRTP, set flags etc. + */ +void zrtp_startZrtpEngine(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->startZrtpEngine(); +} + +void zrtp_stopZrtpEngine(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->stopZrtp(); +} + +void zrtp_processZrtpMessage(ZrtpContext* zrtpContext, uint8_t *extHeader, uint32_t peerSSRC) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->processZrtpMessage(extHeader, peerSSRC); +} + +void zrtp_processTimeout(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->processTimeout(); +} + +//int32_t zrtp_handleGoClear(ZrtpContext* zrtpContext, uint8_t *extHeader) +//{ +// if (zrtpContext && zrtpContext->zrtpEngine) +// return zrtpContext->zrtpEngine->handleGoClear(extHeader) ? 1 : 0; +// +// return 0; +//} + +void zrtp_setAuxSecret(ZrtpContext* zrtpContext, uint8_t* data, int32_t length) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->setAuxSecret(data, length); +} + +int32_t zrtp_inState(ZrtpContext* zrtpContext, int32_t state) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->inState(state) ? 1 : 0; + + return 0; +} + +void zrtp_SASVerified(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->SASVerified(); +} + +void zrtp_resetSASVerified(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->resetSASVerified(); +} + +char* zrtp_getHelloHash(ZrtpContext* zrtpContext) { + std::string ret; + if (zrtpContext && zrtpContext->zrtpEngine) + ret = zrtpContext->zrtpEngine->getHelloHash(); + else + return NULL; + + if (ret.size() == 0) + return NULL; + + char* retval = (char*)malloc(ret.size()+1); + strcpy(retval, ret.c_str()); + return retval; +} + +char* zrtp_getPeerHelloHash(ZrtpContext* zrtpContext) { + std::string ret; + if (zrtpContext && zrtpContext->zrtpEngine) + ret = zrtpContext->zrtpEngine->getPeerHelloHash(); + else + return NULL; + + if (ret.size() == 0) + return NULL; + + char* retval = (char*)malloc(ret.size()+1); + strcpy(retval, ret.c_str()); + return retval; +} + +char* zrtp_getMultiStrParams(ZrtpContext* zrtpContext, int32_t *length) { + std::string ret; + + *length = 0; + if (zrtpContext && zrtpContext->zrtpEngine) + ret = zrtpContext->zrtpEngine->getMultiStrParams(); + else + return NULL; + + if (ret.size() == 0) + return NULL; + + *length = ret.size(); + char* retval = (char*) malloc(ret.size()); + ret.copy(retval, ret.size(), 0); + return retval; +} + +void zrtp_setMultiStrParams(ZrtpContext* zrtpContext, char* parameters, int32_t length) { + if (!zrtpContext || !zrtpContext->zrtpEngine) + return; + + if (parameters == NULL) + return; + + std::string str(""); + str.assign(parameters, length); // set chars (bytes) to the string + + zrtpContext->zrtpEngine->setMultiStrParams(str); +} + +int32_t zrtp_isMultiStream(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->isMultiStream() ? 1 : 0; + + return 0; +} + +int32_t zrtp_isMultiStreamAvailable(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->isMultiStreamAvailable() ? 1 : 0; + + return 0; +} + +void zrtp_acceptEnrollment(ZrtpContext* zrtpContext, int32_t accepted) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->acceptEnrollment(accepted == 0 ? false : true); +} + +int32_t zrtp_isEnrollmentMode(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->isEnrollmentMode() ? 1 : 0; + + return 0; +} + +void zrtp_setEnrollmentMode(ZrtpContext* zrtpContext, int32_t enrollmentMode) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->setEnrollmentMode(enrollmentMode == 0 ? false : true); +} + +int32_t isPeerEnrolled(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->isPeerEnrolled() ? 1 : 0; + + return 0; +} + +int32_t zrtp_sendSASRelayPacket(ZrtpContext* zrtpContext, uint8_t* sh, char* render) { + if (zrtpContext && zrtpContext->zrtpEngine) { + std::string rn(render); + return zrtpContext->zrtpEngine->sendSASRelayPacket(sh, rn) ? 1 : 0; + } + return 0; +} + + +const char* zrtp_getSasType(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) { + std::string rn = zrtpContext->zrtpEngine->getSasType(); + return rn.c_str(); + } + return NULL; +} + + +uint8_t* zrtp_getSasHash(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->getSasHash(); + + return NULL; +} + +int32_t zrtp_setSignatureData(ZrtpContext* zrtpContext, uint8_t* data, int32_t length) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->setSignatureData(data, length) ? 1 : 0; + + return 0; +} + +const uint8_t* zrtp_getSignatureData(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->getSignatureData(); + + return 0; +} + +int32_t zrtp_getSignatureLength(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->getSignatureLength(); + + return 0; +} + +void zrtp_conf2AckSecure(ZrtpContext* zrtpContext) { + if (zrtpContext && zrtpContext->zrtpEngine) + zrtpContext->zrtpEngine->conf2AckSecure(); +} + +int32_t zrtp_getPeerZid(ZrtpContext* zrtpContext, uint8_t* data) { + if (data == NULL) + return 0; + + if (zrtpContext && zrtpContext->zrtpEngine) + return zrtpContext->zrtpEngine->getPeerZid(data); + + return 0; +} + +/* + * The following methods wrap the ZRTP Configure functions + */ +int32_t zrtp_InitializeConfig (ZrtpContext* zrtpContext) +{ + zrtpContext->configure = new ZrtpConfigure(); + return 1; +} + +static EnumBase* getEnumBase(zrtp_AlgoTypes type) +{ + switch(type) { + case zrtp_HashAlgorithm: + return &zrtpHashes; + break; + + case zrtp_CipherAlgorithm: + return &zrtpSymCiphers; + break; + + case zrtp_PubKeyAlgorithm: + return &zrtpPubKeys; + break; + + case zrtp_SasType: + return &zrtpSasTypes; + break; + + case zrtp_AuthLength: + return &zrtpAuthLengths; + break; + + default: + return NULL; + } +} + +char** zrtp_getAlgorithmNames(ZrtpContext* zrtpContext, Zrtp_AlgoTypes type) +{ + std::list<std::string>* names = NULL; + EnumBase* base = getEnumBase(type); + + if (!base) + return NULL; + + names = base->getAllNames(); + int size = base->getSize(); + char** cNames = new char* [size+1]; + cNames[size] = NULL; + + std::list<std::string >::iterator b = names->begin(); + std::list<std::string >::iterator e = names->end(); + + for (int i = 0; b != e; b++, i++) { + cNames[i] = new char [(*b).size()+1]; + strcpy(cNames[i], (*b).c_str()); + } + return cNames; +} + +void zrtp_freeAlgorithmNames(char** names) +{ + if (!names) + return; + + for (char** cp = names; *cp; cp++) + delete *cp; + + delete names; +} + +void zrtp_setStandardConfig(ZrtpContext* zrtpContext) +{ + zrtpContext->configure->setStandardConfig(); +} + +void zrtp_setMandatoryOnly(ZrtpContext* zrtpContext) +{ + zrtpContext->configure->setMandatoryOnly(); +} + +int32_t zrtp_addAlgo(ZrtpContext* zrtpContext, zrtp_AlgoTypes algoType, const char* algo) +{ + EnumBase* base = getEnumBase(algoType); + AlgorithmEnum& a = base->getByName(algo); + + return zrtpContext->configure->addAlgo((AlgoTypes)algoType, a); +} + +int32_t zrtp_addAlgoAt(ZrtpContext* zrtpContext, zrtp_AlgoTypes algoType, const char* algo, int32_t index) +{ + EnumBase* base = getEnumBase(algoType); + AlgorithmEnum& a = base->getByName(algo); + + return zrtpContext->configure->addAlgoAt((AlgoTypes)algoType, a, index); +} + +int32_t zrtp_removeAlgo(ZrtpContext* zrtpContext, zrtp_AlgoTypes algoType, const char* algo) +{ + EnumBase* base = getEnumBase(algoType); + AlgorithmEnum& a = base->getByName(algo); + + return zrtpContext->configure->removeAlgo((AlgoTypes)algoType, a); +} + +int32_t zrtp_getNumConfiguredAlgos(ZrtpContext* zrtpContext, zrtp_AlgoTypes algoType) +{ + return zrtpContext->configure->getNumConfiguredAlgos((AlgoTypes)algoType); +} + +const char* zrtp_getAlgoAt(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, int32_t index) +{ + AlgorithmEnum& a = zrtpContext->configure->getAlgoAt((AlgoTypes)algoType, index); + return a.getName(); +} + +int32_t zrtp_containsAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo) +{ + EnumBase* base = getEnumBase(algoType); + AlgorithmEnum& a = base->getByName(algo); + + return zrtpContext->configure->containsAlgo((AlgoTypes)algoType, a) ? 1 : 0; +} + +void zrtp_setTrustedMitM(ZrtpContext* zrtpContext, int32_t yesNo) +{ + zrtpContext->configure->setTrustedMitM(yesNo ? true : false); +} + +int32_t zrtp_isTrustedMitM(ZrtpContext* zrtpContext) +{ + return zrtpContext->configure->isTrustedMitM() ? 1 : 0; +} + +void zrtp_setSasSignature(ZrtpContext* zrtpContext, int32_t yesNo) +{ + zrtpContext->configure->setSasSignature(yesNo ? true : false); +} + +int32_t zrtp_isSasSignature(ZrtpContext* zrtpContext) +{ + return zrtpContext->configure->isSasSignature() ? 1 : 0; +} diff --git a/src/ZrtpCallbackWrapper.cpp b/src/ZrtpCallbackWrapper.cpp new file mode 100644 index 0000000..ca2dce6 --- /dev/null +++ b/src/ZrtpCallbackWrapper.cpp @@ -0,0 +1,151 @@ +/* + This class maps the ZRTP C++ callback methods to C callback methods. + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#include <libzrtpcpp/ZrtpCallbackWrapper.h> + +ZrtpCallbackWrapper::ZrtpCallbackWrapper(zrtp_Callbacks* cb, ZrtpContext* ctx) : + c_callbacks(cb), zrtpCtx(ctx) +{ + init(); +} + +void ZrtpCallbackWrapper::init() +{ +} +/* +* The following methods implement the GNU ZRTP callback interface. +* For detailed documentation refer to file ZrtpCallback.h +*/ +int32_t ZrtpCallbackWrapper::sendDataZRTP(const unsigned char* data, int32_t length) +{ + return c_callbacks->zrtp_sendDataZRTP(zrtpCtx, data, length); +} + +int32_t ZrtpCallbackWrapper::activateTimer (int32_t time) +{ + c_callbacks->zrtp_activateTimer(zrtpCtx, time); + return 1; +} + +int32_t ZrtpCallbackWrapper::cancelTimer() +{ + c_callbacks->zrtp_cancelTimer(zrtpCtx); + return 0; +} + +void ZrtpCallbackWrapper::sendInfo (GnuZrtpCodes::MessageSeverity severity, int32_t subCode) +{ + c_callbacks->zrtp_sendInfo(zrtpCtx, (int32_t)severity, subCode); +} + +bool ZrtpCallbackWrapper::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part) +{ + C_SrtpSecret_t* cs = new C_SrtpSecret_t; + cs->symEncAlgorithm = (zrtp_SrtpAlgorithms)secrets->symEncAlgorithm; + cs->initKeyLen = secrets->initKeyLen; + cs->initSaltLen = secrets->initSaltLen; + cs->keyInitiator = secrets->keyInitiator; + cs->keyResponder = secrets->keyResponder; + cs->respKeyLen = secrets->respKeyLen; + cs->respSaltLen = secrets->respSaltLen; + cs->role = (int32_t)secrets->role; + cs->saltInitiator = secrets->saltInitiator; + cs->saltResponder = secrets->saltResponder; + cs->sas = new char [secrets->sas.size()+1]; + strcpy(cs->sas, secrets->sas.c_str()); + cs->authAlgorithm = (zrtp_SrtpAlgorithms)secrets->authAlgorithm; + cs->srtpAuthTagLen = secrets->srtpAuthTagLen; + + bool retval = (c_callbacks->zrtp_srtpSecretsReady(zrtpCtx, cs, (int32_t)part) == 0) ? false : true ; + + delete[] cs->sas; + delete cs; + + return retval; +} + +void ZrtpCallbackWrapper::srtpSecretsOff (EnableSecurity part ) +{ + c_callbacks->zrtp_srtpSecretsOff(zrtpCtx, (int32_t)part); +} + +void ZrtpCallbackWrapper::srtpSecretsOn ( std::string c, std::string s, bool verified ) +{ + char* cc = new char [c.size()+1]; + char* cs = new char [s.size()+1]; + + strcpy(cc, c.c_str()); + if(!s.empty()) + strcpy(cs, s.c_str()); + else + *cs = '\0'; + + c_callbacks->zrtp_rtpSecretsOn(zrtpCtx, cc, cs, verified?1:0); + + delete[] cc; + delete[] cs; +} + +void ZrtpCallbackWrapper::handleGoClear() +{ +} + +void ZrtpCallbackWrapper::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) +{ + c_callbacks->zrtp_zrtpNegotiationFailed(zrtpCtx, (int32_t)severity, subCode); +} + +void ZrtpCallbackWrapper::zrtpNotSuppOther() +{ + c_callbacks->zrtp_zrtpNotSuppOther(zrtpCtx); +} + +void ZrtpCallbackWrapper::synchEnter() +{ + c_callbacks->zrtp_synchEnter(zrtpCtx); +} + + +void ZrtpCallbackWrapper::synchLeave() +{ + c_callbacks->zrtp_synchLeave(zrtpCtx); +} + +void ZrtpCallbackWrapper::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) +{ + c_callbacks->zrtp_zrtpAskEnrollment(zrtpCtx, (zrtp_InfoEnrollment)info); +} + +void ZrtpCallbackWrapper::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) +{ + c_callbacks->zrtp_zrtpInformEnrollment(zrtpCtx, (zrtp_InfoEnrollment)info); + +} + +void ZrtpCallbackWrapper::signSAS(uint8_t* sasHash) +{ + c_callbacks->zrtp_signSAS(zrtpCtx, sasHash); +} + +bool ZrtpCallbackWrapper::checkSASSignature(uint8_t* sasHash) +{ + bool retval = (c_callbacks->zrtp_checkSASSignature(zrtpCtx, sasHash) == 0) ? false : true; + + return retval; +} diff --git a/src/ZrtpConfigure.cpp b/src/ZrtpConfigure.cpp new file mode 100644 index 0000000..560466f --- /dev/null +++ b/src/ZrtpConfigure.cpp @@ -0,0 +1,496 @@ +#include <libzrtpcpp/crypto/aesCFB.h>
+#include <libzrtpcpp/crypto/twoCFB.h>
+#include <libzrtpcpp/ZrtpConfigure.h>
+#include <libzrtpcpp/ZrtpTextData.h>
+
+AlgorithmEnum::AlgorithmEnum(const AlgoTypes type, const char* name,
+ int32_t klen, const char* ra, encrypt_t en,
+ decrypt_t de, SrtpAlgorithms alId):
+ algoType(type) , algoName(name), keyLen(klen), readable(ra), encrypt(en),
+ decrypt(de), algoId(alId) {
+}
+
+AlgorithmEnum::~AlgorithmEnum()
+{
+}
+
+const char* AlgorithmEnum::getName() {
+ return algoName.c_str();
+}
+
+const char* AlgorithmEnum::getReadable() {
+ return readable.c_str();
+}
+
+int AlgorithmEnum::getKeylen() {
+ return keyLen;
+}
+
+SrtpAlgorithms AlgorithmEnum::getAlgoId() {
+ return algoId;
+}
+
+encrypt_t AlgorithmEnum::getEncrypt() {
+ return encrypt;
+}
+
+decrypt_t AlgorithmEnum::getDecrypt() {
+ return decrypt;
+}
+
+AlgoTypes AlgorithmEnum::getAlgoType() {
+ return algoType;
+}
+
+bool AlgorithmEnum::isValid() {
+ return (algoType != Invalid);
+}
+
+static AlgorithmEnum invalidAlgo(Invalid, "", 0, "", NULL, NULL, None);
+
+
+EnumBase::EnumBase(AlgoTypes a) : algoType(a) {
+}
+
+EnumBase::~EnumBase() {}
+
+void EnumBase::insert(const char* name) {
+ if (!name)
+ return;
+ AlgorithmEnum* e = new AlgorithmEnum(algoType, name, 0, "", NULL, NULL, None);
+ algos.push_back(e);
+}
+
+void EnumBase::insert(const char* name, int32_t klen, const char* ra,
+ encrypt_t enc, decrypt_t dec, SrtpAlgorithms alId) {
+ if (!name)
+ return;
+ AlgorithmEnum* e = new AlgorithmEnum(algoType, name, klen, ra, enc, dec, alId);
+ algos.push_back(e);
+}
+
+int EnumBase::getSize() {
+ return algos.size();
+}
+
+AlgoTypes EnumBase::getAlgoType() {
+ return algoType;
+}
+
+AlgorithmEnum& EnumBase::getByName(const char* name) {
+ std::vector<AlgorithmEnum* >::iterator b = algos.begin();
+ std::vector<AlgorithmEnum* >::iterator e = algos.end();
+
+ for (; b != e; b++) {
+ if (strncmp((*b)->getName(), name, 4) == 0) {
+ return *(*b);
+ }
+ }
+ return invalidAlgo;
+}
+
+AlgorithmEnum& EnumBase::getByOrdinal(int ord) {
+ std::vector<AlgorithmEnum* >::iterator b = algos.begin();
+ std::vector<AlgorithmEnum* >::iterator e = algos.end();
+
+ for (int i = 0; b != e; ++b) {
+ if (i == ord) {
+ return *(*b);
+ }
+ i++;
+ }
+ return invalidAlgo;
+}
+
+int EnumBase::getOrdinal(AlgorithmEnum& algo) {
+ std::vector<AlgorithmEnum* >::iterator b = algos.begin();
+ std::vector<AlgorithmEnum* >::iterator e = algos.end();
+
+ for (int i = 0; b != e; ++b) {
+ if (strncmp((*b)->getName(), algo.getName(), 4) == 0) {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+}
+
+std::list<std::string>* EnumBase::getAllNames() {
+ std::vector<AlgorithmEnum* >::iterator b = algos.begin();
+ std::vector<AlgorithmEnum* >::iterator e = algos.end();
+
+ std::list<std::string>* strg = new std::list<std::string>();
+
+ for (; b != e; b++) {
+ std::string s((*b)->getName());
+ strg->push_back(s);
+ }
+ return strg;
+}
+
+
+/**
+ * Set up the enumeration list for available hash algorithms
+ */
+HashEnum::HashEnum() : EnumBase(HashAlgorithm) {
+ insert(s256);
+ insert(s384);
+}
+
+HashEnum::~HashEnum() {}
+
+/**
+ * Set up the enumeration list for available symmetric cipher algorithms
+ */
+SymCipherEnum::SymCipherEnum() : EnumBase(CipherAlgorithm) {
+ insert(aes3, 32, "AES-CM-256", aesCfbEncrypt, aesCfbDecrypt, Aes);
+ insert(aes1, 16, "AES-CM-128", aesCfbEncrypt, aesCfbDecrypt, Aes);
+ insert(two3, 32, "TWO-CM-256", twoCfbEncrypt, twoCfbDecrypt, TwoFish);
+ insert(two1, 16, "TWO-CM-128", twoCfbEncrypt, twoCfbDecrypt, TwoFish);
+}
+
+SymCipherEnum::~SymCipherEnum() {}
+
+/**
+ * Set up the enumeration list for available public key algorithms
+ */
+PubKeyEnum::PubKeyEnum() : EnumBase(PubKeyAlgorithm) {
+ insert(dh2k);
+ insert(dh3k);
+ insert(mult);
+ insert(ec25);
+ insert(ec38);
+}
+
+PubKeyEnum::~PubKeyEnum() {}
+
+/**
+ * Set up the enumeration list for available SAS algorithms
+ */
+SasTypeEnum::SasTypeEnum() : EnumBase(SasType) {
+ insert(b32);
+}
+
+SasTypeEnum::~SasTypeEnum() {}
+
+/**
+ * Set up the enumeration list for available SRTP authentications
+ */
+AuthLengthEnum::AuthLengthEnum() : EnumBase(AuthLength) {
+ insert(hs32, 32, "", NULL, NULL, Sha1);
+ insert(hs80, 80, "", NULL, NULL, Sha1);
+ insert(sk32, 32, "", NULL, NULL, Skein);
+ insert(sk64, 64, "", NULL, NULL, Skein);
+}
+
+AuthLengthEnum::~AuthLengthEnum() {}
+
+/*
+ * Here the global accessible enumerations for all implemented algorithms.
+ */
+HashEnum zrtpHashes;
+SymCipherEnum zrtpSymCiphers;
+PubKeyEnum zrtpPubKeys;
+SasTypeEnum zrtpSasTypes;
+AuthLengthEnum zrtpAuthLengths;
+
+/*
+ * The public methods are mainly a facade to the private methods.
+ */
+ZrtpConfigure::ZrtpConfigure() : enableTrustedMitM(false), enableSasSignature(false), enableParanoidMode(false) {}
+
+ZrtpConfigure::~ZrtpConfigure() {}
+
+void ZrtpConfigure::setStandardConfig() {
+ clear();
+
+ addAlgo(HashAlgorithm, zrtpHashes.getByName(s384));
+ addAlgo(HashAlgorithm, zrtpHashes.getByName(s256));
+
+ addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(two3));
+ addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes3));
+ addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(two1));
+ addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes1));
+
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec25));
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh3k));
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec38));
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh2k));
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(mult));
+
+ addAlgo(SasType, zrtpSasTypes.getByName(b32));
+
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(sk32));
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(sk64));
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(hs32));
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(hs80));
+}
+
+void ZrtpConfigure::setMandatoryOnly() {
+ clear();
+
+ addAlgo(HashAlgorithm, zrtpHashes.getByName(s256));
+
+ addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes1));
+
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh3k));
+ addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(mult));
+
+ addAlgo(SasType, zrtpSasTypes.getByName(b32));
+
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(hs32));
+ addAlgo(AuthLength, zrtpAuthLengths.getByName(hs80));
+
+}
+
+void ZrtpConfigure::clear() {
+ hashes.clear();
+ symCiphers.clear();
+ publicKeyAlgos.clear();
+ sasTypes.clear();
+ authLengths.clear();
+}
+
+int32_t ZrtpConfigure::addAlgo(AlgoTypes algoType, AlgorithmEnum& algo) {
+
+ return addAlgo(getEnum(algoType), algo);
+}
+
+int32_t ZrtpConfigure::addAlgoAt(AlgoTypes algoType, AlgorithmEnum& algo, int32_t index) {
+
+ return addAlgoAt(getEnum(algoType), algo, index);
+}
+
+AlgorithmEnum& ZrtpConfigure::getAlgoAt(AlgoTypes algoType, int32_t index) {
+
+ return getAlgoAt(getEnum(algoType), index);
+}
+
+int32_t ZrtpConfigure::removeAlgo(AlgoTypes algoType, AlgorithmEnum& algo) {
+
+ return removeAlgo(getEnum(algoType), algo);
+}
+
+int32_t ZrtpConfigure::getNumConfiguredAlgos(AlgoTypes algoType) {
+
+ return getNumConfiguredAlgos(getEnum(algoType));
+}
+
+bool ZrtpConfigure::containsAlgo(AlgoTypes algoType, AlgorithmEnum& algo) {
+
+ return containsAlgo(getEnum(algoType), algo);
+}
+
+void ZrtpConfigure::printConfiguredAlgos(AlgoTypes algoType) {
+
+ printConfiguredAlgos(getEnum(algoType));
+}
+
+/*
+ * The next methods are the private methods that implement the real
+ * details.
+ */
+AlgorithmEnum& ZrtpConfigure::getAlgoAt(std::vector<AlgorithmEnum* >& a, int32_t index) {
+
+ if (index >= (int)a.size())
+ return invalidAlgo;
+
+ std::vector<AlgorithmEnum* >::iterator b = a.begin();
+ std::vector<AlgorithmEnum* >::iterator e = a.end();
+
+ for (int i = 0; b != e; ++b) {
+ if (i == index) {
+ return *(*b);
+ }
+ i++;
+ }
+ return invalidAlgo;
+}
+
+int32_t ZrtpConfigure::addAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo) {
+ int size = (int)a.size();
+ if (size >= maxNoOfAlgos)
+ return -1;
+
+ if (!algo.isValid())
+ return -1;
+
+ if (containsAlgo(a, algo))
+ return (maxNoOfAlgos - size);
+
+ a.push_back(&algo);
+ return (maxNoOfAlgos - (int)a.size());
+}
+
+int32_t ZrtpConfigure::addAlgoAt(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo, int32_t index) {
+ if (index >= maxNoOfAlgos)
+ return -1;
+
+ int size = (int)a.size();
+
+ if (!algo.isValid())
+ return -1;
+
+// a[index] = &algo;
+
+ if (index >= size) {
+ a.push_back(&algo);
+ return maxNoOfAlgos - (int)a.size();
+ }
+ std::vector<AlgorithmEnum* >::iterator b = a.begin();
+ std::vector<AlgorithmEnum* >::iterator e = a.end();
+
+ for (int i = 0; b != e; ++b) {
+ if (i == index) {
+ a.insert(b, &algo);
+ break;
+ }
+ i++;
+ }
+ return (maxNoOfAlgos - (int)a.size());
+}
+
+int32_t ZrtpConfigure::removeAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo) {
+
+ if ((int)a.size() == 0 || !algo.isValid())
+ return maxNoOfAlgos;
+
+ std::vector<AlgorithmEnum* >::iterator b = a.begin();
+ std::vector<AlgorithmEnum* >::iterator e = a.end();
+
+ for (; b != e; ++b) {
+ if (strcmp((*b)->getName(), algo.getName()) == 0) {
+ a.erase(b);
+ break;
+ }
+ }
+ return (maxNoOfAlgos - (int)a.size());
+}
+
+int32_t ZrtpConfigure::getNumConfiguredAlgos(std::vector<AlgorithmEnum* >& a) {
+ return (int32_t)a.size();
+}
+
+bool ZrtpConfigure::containsAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo) {
+
+ if ((int)a.size() == 0 || !algo.isValid())
+ return false;
+
+ std::vector<AlgorithmEnum* >::iterator b = a.begin();
+ std::vector<AlgorithmEnum* >::iterator e = a.end();
+
+ for (; b != e; ++b) {
+ if (strcmp((*b)->getName(), algo.getName()) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ZrtpConfigure::printConfiguredAlgos(std::vector<AlgorithmEnum* >& a) {
+
+ std::vector<AlgorithmEnum* >::iterator b = a.begin();
+ std::vector<AlgorithmEnum* >::iterator e = a.end();
+
+ for (; b != e; ++b) {
+ printf("print configured: name: %s\n", (*b)->getName());
+ }
+}
+
+std::vector<AlgorithmEnum* >& ZrtpConfigure::getEnum(AlgoTypes algoType) {
+
+ switch(algoType) {
+ case HashAlgorithm:
+ return hashes;
+ break;
+
+ case CipherAlgorithm:
+ return symCiphers;
+ break;
+
+ case PubKeyAlgorithm:
+ return publicKeyAlgos;
+ break;
+
+ case SasType:
+ return sasTypes;
+ break;
+
+ case AuthLength:
+ return authLengths;
+ break;
+
+ default:
+ break;
+ }
+ return hashes;
+}
+
+void ZrtpConfigure::setTrustedMitM(bool yesNo) {
+ enableTrustedMitM = yesNo;
+}
+
+bool ZrtpConfigure::isTrustedMitM() {
+ return enableTrustedMitM;
+}
+
+void ZrtpConfigure::setSasSignature(bool yesNo) {
+ enableSasSignature = yesNo;
+}
+
+bool ZrtpConfigure::isSasSignature() {
+ return enableSasSignature;
+}
+
+void ZrtpConfigure::setParanoidMode(bool yesNo) {
+ enableParanoidMode = yesNo;
+}
+
+bool ZrtpConfigure::isParanoidMode() {
+ return enableParanoidMode;
+}
+
+#if 0
+ZrtpConfigure config;
+
+main() {
+ printf("Start\n");
+ printf("size: %d\n", zrtpHashes.getSize());
+ AlgorithmEnum e = zrtpHashes.getByName("S256");
+ printf("algo name: %s\n", e.getName());
+ printf("algo type: %d\n", e.getAlgoType());
+
+ std::list<std::string>* names = zrtpHashes.getAllNames();
+ printf("size of name list: %d\n", names->size());
+ printf("first name: %s\n", names->front().c_str());
+ printf("last name: %s\n", names->back().c_str());
+
+ printf("free slots: %d (expected 6)\n", config.addAlgo(HashAlgorithm, e));
+
+ AlgorithmEnum e1(HashAlgorithm, "SHA384");
+ printf("free slots: %d (expected 5)\n", config.addAlgoAt(HashAlgorithm, e1, 0));
+ AlgorithmEnum e2 = config.getAlgoAt(HashAlgorithm, 0);
+ printf("algo name: %s (expected SHA384)\n", e2.getName());
+ printf("Num of configured algos: %d (expected 2)\n", config.getNumConfiguredAlgos(HashAlgorithm));
+ config.printConfiguredAlgos(HashAlgorithm);
+ printf("free slots: %d (expected 6)\n", config.removeAlgo(HashAlgorithm, e2));
+ e2 = config.getAlgoAt(HashAlgorithm, 0);
+ printf("algo name: %s (expected SHA256)\n", e2.getName());
+
+ printf("clearing config\n");
+ config.clear();
+ printf("size: %d\n", zrtpHashes.getSize());
+ e = zrtpHashes.getByName("S256");
+ printf("algo name: %s\n", e.getName());
+ printf("algo type: %d\n", e.getAlgoType());
+
+}
+
+#endif
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-default-style: ellemtel
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/src/ZrtpCrc32.cpp b/src/ZrtpCrc32.cpp new file mode 100644 index 0000000..c65b19a --- /dev/null +++ b/src/ZrtpCrc32.cpp @@ -0,0 +1,223 @@ +/* SCTP kernel reference Implementation + * Copyright (c) 1999-2001 Motorola, Inc. + * Copyright (c) 2001-2003 International Business Machines, Corp. + * + * SCTP Checksum functions + * + * The SCTP reference implementation 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, or (at your option) + * any later version. + * + * The SCTP reference implementation 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 GNU CC; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers <lksctp-developers@lists.sourceforge.net> + * + * Or submit a bug report through the following website: + * http://www.sf.net/projects/lksctp + * + * Written or modified by: + * Dinakaran Joseph + * Jon Grimm <jgrimm@us.ibm.com> + * Sridhar Samudrala <sri@us.ibm.com> + * + * Any bugs reported given to us we will try to fix... any fixes shared will + * be incorporated into the next SCTP release. + */ + +/* The following code has been taken directly from + * draft-ietf-tsvwg-sctpcsum-03.txt + * + * The code has now been modified by Werner.Dittmann@t-online.de for use + * inside the ZRTP implementation. + */ + +#include <stdio.h> +#include <stdint.h> +#include <libzrtpcpp/ZrtpCrc32.h> + +#define CRC32C_POLY 0x1EDC6F41 +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Copyright 2001, D. Otis. Use this program, code or tables */ +/* extracted from it, as desired without restriction. */ +/* */ +/* 32 Bit Reflected CRC table generation for SCTP. */ +/* To accommodate serial byte data being shifted out least */ +/* significant bit first, the table's 32 bit words are reflected */ +/* which flips both byte and bit MS and LS positions. The CRC */ +/* is calculated MS bits first from the perspective of the serial*/ +/* stream. The x^32 term is implied and the x^0 term may also */ +/* be shown as +1. The polynomial code used is 0x1EDC6F41. */ +/* Castagnoli93 */ +/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */ +/* x^11+x^10+x^9+x^8+x^6+x^0 */ +/* Guy Castagnoli Stefan Braeuer and Martin Herrman */ +/* "Optimization of Cyclic Redundancy-Check Codes */ +/* with 24 and 32 Parity Bits", */ +/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static const uint32_t crc_c[256] = { + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, + 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, + 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, + 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, + 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, + 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, + 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, + 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, + 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, + 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, + 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, + 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, + 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, + 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, + 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, + 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, + 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, + 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, + 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, + 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, + 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, + 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, + 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, + 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, + 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, + 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, + 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, + 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, + 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, + 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, + 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, + 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, + 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, +}; + + +bool zrtpCheckCksum(uint8_t *buffer, uint16_t length, uint32_t crc32) +{ + uint32_t chksum = zrtpGenerateCksum(buffer, length); + chksum = zrtpEndCksum(chksum); + // fprintf(stderr, "Received crc %x, computed crc: %x\n", crc32, chksum); + return (crc32 == chksum); +} + +uint32_t zrtpGenerateCksum(uint8_t *buffer, uint16_t length) +{ + uint32_t crc32 = ~(uint32_t) 0; + uint32_t i; + + // fprintf(stderr, "Buffer %xl, length: %d\n", buffer, length); + /* Calculate the CRC. */ + for (i = 0; i < length ; i++) + CRC32C(crc32, buffer[i]); + + return crc32; +} + +uint32_t zrtpEndCksum(uint32_t crc32) +{ + uint32_t result; + uint8_t byte0, byte1, byte2, byte3; + + result = ~crc32; + + /* result now holds the negated polynomial remainder; + * since the table and algorithm is "reflected" [williams95]. + * That is, result has the same value as if we mapped the message + * to a polyomial, computed the host-bit-order polynomial + * remainder, performed final negation, then did an end-for-end + * bit-reversal. + * Note that a 32-bit bit-reversal is identical to four inplace + * 8-bit reversals followed by an end-for-end byteswap. + * In other words, the bytes of each bit are in the right order, + * but the bytes have been byteswapped. So we now do an explicit + * byteswap. On a little-endian machine, this byteswap and + * the final ntohl cancel out and could be elided. + */ + byte0 = result & 0xff; + byte1 = (result>>8) & 0xff; + byte2 = (result>>16) & 0xff; + byte3 = (result>>24) & 0xff; + + crc32 = ((byte0 << 24) | + (byte1 << 16) | + (byte2 << 8) | + byte3); + // fprintf(stderr, "Computed crc32: %x\n", crc32); + return crc32; +} + +#ifdef UNIT_TEST +uint8_t test_data[48] = { + 0x01, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0xFE, 0x60, 0xAC, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x09, + 0x25, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +int main( int argc, char * argv[] ) +{ + crc32c = sctp_update_cksum(test_data, 48); + printf("Hello World, expected result: 0x664f75eb\n"); + printf("Result is: 0x%x\n", crc32c); +} +#endif +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/ZrtpPacketClearAck.cpp b/src/ZrtpPacketClearAck.cpp new file mode 100644 index 0000000..85f1484 --- /dev/null +++ b/src/ZrtpPacketClearAck.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketClearAck.h> + +ZrtpPacketClearAck::ZrtpPacketClearAck() { + DEBUGOUT((fprintf(stdout, "Creating ClearAck packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + + setZrtpId(); + setLength((sizeof(ClearAckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)ClearAckMsg); +} + +ZrtpPacketClearAck::ZrtpPacketClearAck(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating Conf2Ack packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((ClearAckPacket_t*)data)->hdr; // the standard header +} + +ZrtpPacketClearAck::~ZrtpPacketClearAck() { + DEBUGOUT((fprintf(stdout, "Deleting ClearAck packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketCommit.cpp b/src/ZrtpPacketCommit.cpp new file mode 100644 index 0000000..97a162a --- /dev/null +++ b/src/ZrtpPacketCommit.cpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketCommit.h> + +ZrtpPacketCommit::ZrtpPacketCommit() { + DEBUGOUT((fprintf(stdout, "Creating commit packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + commitHeader = &data.commit; + + setZrtpId(); + setLength((sizeof (CommitPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)CommitMsg); +} + +void ZrtpPacketCommit::setNonce(uint8_t* text) { + memcpy(commitHeader->hvi, text, sizeof(commitHeader->hvi) - 4 * ZRTP_WORD_SIZE); + uint16_t len = getLength(); + len -= 4; + setLength(len); +} + +ZrtpPacketCommit::ZrtpPacketCommit(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating commit packet from data\n"))); + zrtpHeader = (zrtpPacketHeader_t *)&((CommitPacket_t *)data)->hdr; // the standard header + commitHeader = (Commit_t *)&((CommitPacket_t *)data)->commit; +} + +ZrtpPacketCommit::~ZrtpPacketCommit() { + DEBUGOUT((fprintf(stdout, "Deleting commit packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketConf2Ack.cpp b/src/ZrtpPacketConf2Ack.cpp new file mode 100644 index 0000000..f35dc82 --- /dev/null +++ b/src/ZrtpPacketConf2Ack.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketConf2Ack.h> + +ZrtpPacketConf2Ack::ZrtpPacketConf2Ack() { + DEBUGOUT((fprintf(stdout, "Creating Conf2Ack packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + + setZrtpId(); + setLength((sizeof (Conf2AckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)Conf2AckMsg); +} + +ZrtpPacketConf2Ack::ZrtpPacketConf2Ack(char *data) { + DEBUGOUT((fprintf(stdout, "Creating Conf2Ack packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((Conf2AckPacket_t*)data)->hdr; // the standard header +} + +ZrtpPacketConf2Ack::~ZrtpPacketConf2Ack() { + DEBUGOUT((fprintf(stdout, "Deleting Conf2Ack packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketConfirm.cpp b/src/ZrtpPacketConfirm.cpp new file mode 100644 index 0000000..f558759 --- /dev/null +++ b/src/ZrtpPacketConfirm.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketConfirm.h> + +ZrtpPacketConfirm::ZrtpPacketConfirm() { + DEBUGOUT((fprintf(stdout, "Creating Confirm packet without data, no sl data\n"))); + initialize(); + setSignatureLength(0); +} + +ZrtpPacketConfirm::ZrtpPacketConfirm(uint32_t sl) { + DEBUGOUT((fprintf(stdout, "Creating Confirm packet without data\n"))); + initialize(); + setSignatureLength(sl); +} + +void ZrtpPacketConfirm::initialize() { + void* allocated = &data; + memset(allocated, 0, sizeof(data)); + + zrtpHeader = (zrtpPacketHeader_t *)&((ConfirmPacket_t *)allocated)->hdr; // the standard header + confirmHeader = (Confirm_t *)&((ConfirmPacket_t *)allocated)->confirm; + + setZrtpId(); +} + +bool ZrtpPacketConfirm::setSignatureLength(uint32_t sl) { + if (sl > 512) + return false; + + int32_t length = sizeof(ConfirmPacket_t) + (sl * ZRTP_WORD_SIZE); + confirmHeader->sigLength = sl; // sigLength is a uint byte + if (sl & 0x100) { // check the 9th bit + confirmHeader->filler[1] = 1; // and set it if necessary + } + setLength(length / 4); + return true; +} + +bool ZrtpPacketConfirm::setSignatureData(uint8_t* data, int32_t length) { + int32_t l = getSignatureLength() * 4; + if (length > l || (length % 4) != 0) + return false; + + uint8_t* p = ((uint8_t*)&confirmHeader->expTime) + 4; // point to signature block + memcpy(p, data, length); + return true; +} + +int32_t ZrtpPacketConfirm::getSignatureLength() { + int32_t sl = confirmHeader->sigLength & 0xff; + if (confirmHeader->filler[1] == 1) { // do we have a 9th bit + sl |= 0x100; + } + return sl; +} + +ZrtpPacketConfirm::ZrtpPacketConfirm(uint8_t* data) { + DEBUGOUT((fprintf(stdout, "Creating Confirm packet from data\n"))); + + allocated = NULL; + zrtpHeader = (zrtpPacketHeader_t *)&((ConfirmPacket_t *)data)->hdr; // the standard header + confirmHeader = (Confirm_t *)&((ConfirmPacket_t *)data)->confirm; +} + +ZrtpPacketConfirm::~ZrtpPacketConfirm() { + DEBUGOUT((fprintf(stdout, "Deleting Confirm packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketDHPart.cpp b/src/ZrtpPacketDHPart.cpp new file mode 100644 index 0000000..8c59233 --- /dev/null +++ b/src/ZrtpPacketDHPart.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketDHPart.h> + +ZrtpPacketDHPart::ZrtpPacketDHPart() { + DEBUGOUT((fprintf(stdout, "Creating DHPart packet without data and pkt type\n"))); + initialize(); +} + +ZrtpPacketDHPart::ZrtpPacketDHPart(const char* pkt) { + DEBUGOUT((fprintf(stdout, "Creating DHPart packet without data\n"))); + initialize(); + setPubKeyType(pkt); +} + +void ZrtpPacketDHPart::initialize() { + + void* allocated = &data; + memset(allocated, 0, sizeof(data)); + + zrtpHeader = (zrtpPacketHeader_t *)&((DHPartPacket_t *)allocated)->hdr; // the standard header + DHPartHeader = (DHPart_t *)&((DHPartPacket_t *)allocated)->dhPart; + pv = ((uint8_t*)allocated) + sizeof(DHPartPacket_t); // point to the public key value + + setZrtpId(); +} + +// The fixed numbers below are taken from ZRTP specification, chap 5.1.5 +void ZrtpPacketDHPart::setPubKeyType(const char* pkt) { + // Well - the algo type is only 4 char thus cast to int32 and compare + if (*(int32_t*)pkt == *(int32_t*)dh2k) { + dhLength = 256; + } + else if (*(int32_t*)pkt == *(int32_t*)dh3k) { + dhLength = 384; + } + else if (*(int32_t*)pkt == *(int32_t*)ec25) { + dhLength = 64; + } + else if (*(int32_t*)pkt == *(int32_t*)ec38) { + dhLength = 96; + } + else + return; + + int length = sizeof(DHPartPacket_t) + dhLength + (2 * ZRTP_WORD_SIZE); // HMAC field is 2*ZRTP_WORD_SIZE + setLength(length / ZRTP_WORD_SIZE); +} + +ZrtpPacketDHPart::ZrtpPacketDHPart(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating DHPart packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((DHPartPacket_t *)data)->hdr; // the standard header + DHPartHeader = (DHPart_t *)&((DHPartPacket_t *)data)->dhPart; + + int16_t len = getLength(); + DEBUGOUT((fprintf(stdout, "DHPart length: %d\n", len))); + if (len == 85) { + dhLength = 256; + } + else if (len == 117) { + dhLength = 384; + } + else if (len == 37) { + dhLength = 64; + } + else if (len == 45) { + dhLength = 96; + } + else { + pv = NULL; + return; + } + pv = data + sizeof(DHPartPacket_t); // point to the public key value +} + +ZrtpPacketDHPart::~ZrtpPacketDHPart() { + DEBUGOUT((fprintf(stdout, "Deleting DHPart packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketError.cpp b/src/ZrtpPacketError.cpp new file mode 100644 index 0000000..a9d881e --- /dev/null +++ b/src/ZrtpPacketError.cpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Copyright (C) 2006 + * + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketError.h> + +ZrtpPacketError::ZrtpPacketError() { + DEBUGOUT((fprintf(stdout, "Creating Error packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + errorHeader = &data.error; + + setZrtpId(); + setLength((sizeof(ErrorPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)ErrorMsg); +} + +ZrtpPacketError::ZrtpPacketError(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating Error packet from data\n"))); + + allocated = NULL; + zrtpHeader = (zrtpPacketHeader_t *)&((ErrorPacket_t *)data)->hdr; // the standard header + errorHeader = (Error_t *)&((ErrorPacket_t *)data)->error; +} + +ZrtpPacketError::~ZrtpPacketError() { + DEBUGOUT((fprintf(stdout, "Deleting Error packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketErrorAck.cpp b/src/ZrtpPacketErrorAck.cpp new file mode 100644 index 0000000..3a30977 --- /dev/null +++ b/src/ZrtpPacketErrorAck.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketErrorAck.h> + +ZrtpPacketErrorAck::ZrtpPacketErrorAck() { + DEBUGOUT((fprintf(stdout, "Creating ErrorAck packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + + setZrtpId(); + setLength((sizeof (ErrorAckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)ErrorAckMsg); +} + +ZrtpPacketErrorAck::ZrtpPacketErrorAck(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating ErrorAck packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((ErrorAckPacket_t*)data)->hdr; // the standard header +} + +ZrtpPacketErrorAck::~ZrtpPacketErrorAck() { + DEBUGOUT((fprintf(stdout, "Deleting ErrorAck packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketGoClear.cpp b/src/ZrtpPacketGoClear.cpp new file mode 100644 index 0000000..247aec1 --- /dev/null +++ b/src/ZrtpPacketGoClear.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Copyright (C) 2006 + * + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketGoClear.h> + +ZrtpPacketGoClear::ZrtpPacketGoClear() { + DEBUGOUT((fprintf(stdout, "Creating GoClear packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + clearHeader = &data.goClear; + + setZrtpId(); + setLength((sizeof(GoClearPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)GoClearMsg); +} + +ZrtpPacketGoClear::ZrtpPacketGoClear(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating GoClear packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((GoClearPacket_t *)data)->hdr; // the standard header + clearHeader = (GoClear_t *)&((GoClearPacket_t *)data)->goClear; +} + +ZrtpPacketGoClear::~ZrtpPacketGoClear() { + DEBUGOUT((fprintf(stdout, "Deleting GoClear packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketHello.cpp b/src/ZrtpPacketHello.cpp new file mode 100644 index 0000000..17a82d2 --- /dev/null +++ b/src/ZrtpPacketHello.cpp @@ -0,0 +1,129 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketHello.h> + + +ZrtpPacketHello::ZrtpPacketHello() { + DEBUGOUT((fprintf(stdout, "Creating Hello packet without data\n"))); +} + +void ZrtpPacketHello::configureHello(ZrtpConfigure* config) { + // The NumSupported* data is in ZrtpTextData.h + nHash = config->getNumConfiguredAlgos(HashAlgorithm); + nCipher = config->getNumConfiguredAlgos(CipherAlgorithm); + nPubkey = config->getNumConfiguredAlgos(PubKeyAlgorithm); + nSas = config->getNumConfiguredAlgos(SasType); + nAuth = config->getNumConfiguredAlgos(AuthLength); + + // length is fixed Header plus HMAC size (2*ZRTP_WORD_SIZE) + int32_t length = sizeof(HelloPacket_t) + (2 * ZRTP_WORD_SIZE); + length += nHash * ZRTP_WORD_SIZE; + length += nCipher * ZRTP_WORD_SIZE; + length += nPubkey * ZRTP_WORD_SIZE; + length += nSas * ZRTP_WORD_SIZE; + length += nAuth * ZRTP_WORD_SIZE; + + // Don't change order of this sequence + oHash = sizeof(Hello_t); + oCipher = oHash + (nHash * ZRTP_WORD_SIZE); + oAuth = oCipher + (nCipher * ZRTP_WORD_SIZE); + oPubkey = oAuth + (nAuth * ZRTP_WORD_SIZE); + oSas = oPubkey + (nPubkey * ZRTP_WORD_SIZE); + oHmac = oSas + (nSas * ZRTP_WORD_SIZE); // offset to HMAC + + void* allocated = &data; + memset(allocated, 0, sizeof(data)); + + zrtpHeader = (zrtpPacketHeader_t *)&((HelloPacket_t *)allocated)->hdr; // the standard header + helloHeader = (Hello_t *)&((HelloPacket_t *)allocated)->hello; + + setZrtpId(); + + // minus 1 for CRC size + setLength(length / ZRTP_WORD_SIZE); + setMessageType((uint8_t*)HelloMsg); + + setVersion((uint8_t*)zrtpVersion); + + uint32_t lenField = nHash << 16; + for (int32_t i = 0; i < nHash; i++) { + AlgorithmEnum& hash = config->getAlgoAt(HashAlgorithm, i); + setHashType(i, (int8_t*)hash.getName()); + } + + lenField |= nCipher << 12; + for (int32_t i = 0; i < nCipher; i++) { + AlgorithmEnum& cipher = config->getAlgoAt(CipherAlgorithm, i); + setCipherType(i, (int8_t*)cipher.getName()); + } + + lenField |= nAuth << 8; + for (int32_t i = 0; i < nAuth; i++) { + AlgorithmEnum& length = config->getAlgoAt(AuthLength, i); + setAuthLen(i, (int8_t*)length.getName()); + } + + lenField |= nPubkey << 4; + for (int32_t i = 0; i < nPubkey; i++) { + AlgorithmEnum& pubKey = config->getAlgoAt(PubKeyAlgorithm, i); + setPubKeyType(i, (int8_t*)pubKey.getName()); + } + + lenField |= nSas; + for (int32_t i = 0; i < nSas; i++) { + AlgorithmEnum& sas = config->getAlgoAt(SasType, i); + setSasType(i, (int8_t*)sas.getName()); + } + *((uint32_t*)&helloHeader->flags) = htonl(lenField); +} + +ZrtpPacketHello::ZrtpPacketHello(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating Hello packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((HelloPacket_t *)data)->hdr; // the standard header + helloHeader = (Hello_t *)&((HelloPacket_t *)data)->hello; + + uint32_t t = *((uint32_t*)&helloHeader->flags); + uint32_t temp = ntohl(t); + + nHash = (temp & (0xf << 16)) >> 16; + nHash &= 0x7; // restrict to max 7 algorithms + nCipher = (temp & (0xf << 12)) >> 12; + nCipher &= 0x7; + nAuth = (temp & (0xf << 8)) >> 8; + nAuth &= 0x7; + nPubkey = (temp & (0xf << 4)) >> 4; + nPubkey &= 0x7; + nSas = temp & 0xf; + nSas &= 0x7; + + oHash = sizeof(Hello_t); + oCipher = oHash + (nHash * ZRTP_WORD_SIZE); + oAuth = oCipher + (nCipher * ZRTP_WORD_SIZE); + oPubkey = oAuth + (nAuth * ZRTP_WORD_SIZE); + oSas = oPubkey + (nPubkey * ZRTP_WORD_SIZE); + oHmac = oSas + (nSas * ZRTP_WORD_SIZE); // offset to HMAC +} + +ZrtpPacketHello::~ZrtpPacketHello() { + DEBUGOUT((fprintf(stdout, "Deleting Hello packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketHelloAck.cpp b/src/ZrtpPacketHelloAck.cpp new file mode 100644 index 0000000..2d752b7 --- /dev/null +++ b/src/ZrtpPacketHelloAck.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketHelloAck.h> + +ZrtpPacketHelloAck::ZrtpPacketHelloAck() { + DEBUGOUT((fprintf(stdout, "Creating HelloAck packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + + setZrtpId(); + setLength((sizeof(HelloAckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)HelloAckMsg); +} + +ZrtpPacketHelloAck::ZrtpPacketHelloAck(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating HelloAck packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((HelloAckPacket_t *)data)->hdr; // the standard header + +} + +ZrtpPacketHelloAck::~ZrtpPacketHelloAck() { + DEBUGOUT((fprintf(stdout, "Deleting HelloAck packet: alloc: %x\n", allocated))); +} + diff --git a/src/ZrtpPacketPing.cpp b/src/ZrtpPacketPing.cpp new file mode 100644 index 0000000..9a1f90f --- /dev/null +++ b/src/ZrtpPacketPing.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketPing.h> + +ZrtpPacketPing::ZrtpPacketPing() { + DEBUGOUT((fprintf(stdout, "Creating Ping packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + pingHeader = &data.ping; + + setZrtpId(); + setLength((sizeof(PingPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)PingMsg); + setVersion((uint8_t*)zrtpVersion); +} + +ZrtpPacketPing::ZrtpPacketPing(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating Ping packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((PingPacket_t*)data)->hdr; // the standard header + pingHeader = (Ping_t *)&((PingPacket_t *)data)->ping; +} + +ZrtpPacketPing::~ZrtpPacketPing() { + DEBUGOUT((fprintf(stdout, "Deleting Ping packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketPingAck.cpp b/src/ZrtpPacketPingAck.cpp new file mode 100644 index 0000000..2331640 --- /dev/null +++ b/src/ZrtpPacketPingAck.cpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketPingAck.h> + +ZrtpPacketPingAck::ZrtpPacketPingAck() { + DEBUGOUT((fprintf(stdout, "Creating PingAck packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + pingAckHeader = &data.pingAck; + + setZrtpId(); + setLength((sizeof(PingAckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)PingAckMsg); + setVersion((uint8_t*)zrtpVersion); +} + +ZrtpPacketPingAck::ZrtpPacketPingAck(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating PingAck packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((PingAckPacket_t*)data)->hdr; // the standard header + pingAckHeader = (PingAck_t *)&((PingAckPacket_t *)data)->pingAck; +} + +ZrtpPacketPingAck::~ZrtpPacketPingAck() { + DEBUGOUT((fprintf(stdout, "Deleting PingAck packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketRelayAck.cpp b/src/ZrtpPacketRelayAck.cpp new file mode 100644 index 0000000..6ff0c7a --- /dev/null +++ b/src/ZrtpPacketRelayAck.cpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2006-20011 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketRelayAck.h> + +ZrtpPacketRelayAck::ZrtpPacketRelayAck() { + DEBUGOUT((fprintf(stdout, "Creating RelayAck packet without data\n"))); + + zrtpHeader = &data.hdr; // the standard header + + setZrtpId(); + setLength((sizeof (RelayAckPacket_t) / ZRTP_WORD_SIZE) - 1); + setMessageType((uint8_t*)RelayAckMsg); +} + +ZrtpPacketRelayAck::ZrtpPacketRelayAck(uint8_t *data) { + DEBUGOUT((fprintf(stdout, "Creating RelayAck packet from data\n"))); + + zrtpHeader = (zrtpPacketHeader_t *)&((RelayAckPacket_t*)data)->hdr; // the standard header +} + +ZrtpPacketRelayAck::~ZrtpPacketRelayAck() { + DEBUGOUT((fprintf(stdout, "Deleting RelayAck packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpPacketSASrelay.cpp b/src/ZrtpPacketSASrelay.cpp new file mode 100644 index 0000000..d132e28 --- /dev/null +++ b/src/ZrtpPacketSASrelay.cpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <libzrtpcpp/ZrtpPacketSASrelay.h> + +ZrtpPacketSASrelay::ZrtpPacketSASrelay() { + DEBUGOUT((fprintf(stdout, "Creating SASrelay packet without data, no sl data\n"))); + initialize(); + setSignatureLength(0); +} + +ZrtpPacketSASrelay::ZrtpPacketSASrelay(uint32_t sl) { + DEBUGOUT((fprintf(stdout, "Creating SASrelay packet without data\n"))); + initialize(); + setSignatureLength(sl); +} + +void ZrtpPacketSASrelay::initialize() { + void* allocated = &data; + memset(allocated, 0, sizeof(data)); + + zrtpHeader = (zrtpPacketHeader_t *)&((SASrelayPacket_t *)allocated)->hdr; // the standard header + sasRelayHeader = (SASrelay_t *)&((SASrelayPacket_t *)allocated)->sasrelay; + + setZrtpId(); + setMessageType((uint8_t*)SasRelayMsg); +} + +void ZrtpPacketSASrelay::setSignatureLength(uint32_t sl) { + sl &= 0x1ff; // make sure it is max 9 bits + int32_t length = sizeof(ConfirmPacket_t) + (sl * ZRTP_WORD_SIZE); + sasRelayHeader->sigLength = sl; // sigLength is a uint byte + if (sl & 0x100) { // check the 9th bit + sasRelayHeader->filler[1] = 1; // and set it if necessary + } + setLength(length / 4); +} + +uint32_t ZrtpPacketSASrelay::getSignatureLength() { + uint32_t sl = sasRelayHeader->sigLength; + if (sasRelayHeader->filler[1] == 1) { // do we have a 9th bit + sl |= 0x100; + } + return sl; +} + +ZrtpPacketSASrelay::ZrtpPacketSASrelay(uint8_t* data) { + DEBUGOUT((fprintf(stdout, "Creating SASrelay packet from data\n"))); + + allocated = NULL; + zrtpHeader = (zrtpPacketHeader_t *)&((SASrelayPacket_t *)data)->hdr; // the standard header + sasRelayHeader = (SASrelay_t *)&((SASrelayPacket_t *)data)->sasrelay; +} + +ZrtpPacketSASrelay::~ZrtpPacketSASrelay() { + DEBUGOUT((fprintf(stdout, "Deleting SASrelay packet: alloc: %x\n", allocated))); +} diff --git a/src/ZrtpQueue.cpp b/src/ZrtpQueue.cpp new file mode 100644 index 0000000..c9cd0e2 --- /dev/null +++ b/src/ZrtpQueue.cpp @@ -0,0 +1,847 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <string> +#include <stdio.h> + +#include <libzrtpcpp/ZrtpQueue.h> +#include <libzrtpcpp/ZIDFile.h> +#include <libzrtpcpp/ZRtp.h> +#include <libzrtpcpp/ZrtpStateClass.h> +#include <libzrtpcpp/ZrtpUserCallback.h> + +static TimeoutProvider<std::string, ost::ZrtpQueue*>* staticTimeoutProvider = NULL; + +NAMESPACE_COMMONCPP +using namespace GnuZrtpCodes; + +ZrtpQueue::ZrtpQueue(uint32 size, RTPApplication& app) : + AVPQueue(size,app) +{ + init(); +} + +ZrtpQueue::ZrtpQueue(uint32 ssrc, uint32 size, RTPApplication& app) : + AVPQueue(ssrc,size,app) +{ + init(); +} + +void ZrtpQueue::init() +{ + zrtpUserCallback = NULL; + enableZrtp = false; + started = false; + mitmMode = false; + enableParanoidMode = false; + zrtpEngine = NULL; + senderZrtpSeqNo = 1; + + clientIdString = clientId; + peerSSRC = 0; +} + +ZrtpQueue::~ZrtpQueue() { + + endQueue(); + stopZrtp(); + + if (zrtpUserCallback != NULL) { + delete zrtpUserCallback; + zrtpUserCallback = NULL; + } +} + +int32_t +ZrtpQueue::initialize(const char *zidFilename, bool autoEnable, ZrtpConfigure* config) +{ + int32_t ret = 1; + + synchEnter(); + + ZrtpConfigure* configOwn = NULL; + if (config == NULL) { + config = configOwn = new ZrtpConfigure(); + config->setStandardConfig(); + } + enableZrtp = autoEnable; + + config->setParanoidMode(enableParanoidMode); + + if (staticTimeoutProvider == NULL) { + staticTimeoutProvider = new TimeoutProvider<std::string, ZrtpQueue*>(); + staticTimeoutProvider->start(); + } + ZIDFile* zf = ZIDFile::getInstance(); + if (!zf->isOpen()) { + std::string fname; + if (zidFilename == NULL) { + char *home = getenv("HOME"); + std::string baseDir = (home != NULL) ? (std::string(home) + std::string("/.")) + : std::string("."); + fname = baseDir + std::string("GNUZRTP.zid"); + zidFilename = fname.c_str(); + } + if (zf->open((char *)zidFilename) < 0) { + enableZrtp = false; + ret = -1; + } + } + if (ret > 0) { + const uint8_t* ownZid = zf->getZid(); + zrtpEngine = new ZRtp((uint8_t*)ownZid, (ZrtpCallback*)this, clientIdString, config, mitmMode, signSas); + } + if (configOwn != NULL) { + delete configOwn; + } + synchLeave(); + return ret; +} + +void ZrtpQueue::startZrtp() { + if (zrtpEngine != NULL) { + zrtpEngine->startZrtpEngine(); + started = true; + } +} + +void ZrtpQueue::stopZrtp() { + if (zrtpEngine != NULL) { + delete zrtpEngine; + zrtpEngine = NULL; + started = false; + } +} + +/* + * The takeInDataPacket implementation for ZRTPQueue. + */ +size_t +ZrtpQueue::takeInDataPacket(void) +{ + InetHostAddress network_address; + tpport_t transport_port; + + uint32 nextSize = (uint32)getNextDataPacketSize(); + unsigned char* buffer = new unsigned char[nextSize]; + int32 rtn = (int32)recvData(buffer, nextSize, network_address, transport_port); + if ( (rtn < 0) || ((uint32)rtn > getMaxRecvPacketSize()) ){ + delete buffer; + return 0; + } + + IncomingZRTPPkt* packet = NULL; + // check if this could be a real RTP/SRTP packet. + if ((*buffer & 0xf0) != 0x10) { + return (rtpDataPacket(buffer, rtn, network_address, transport_port)); + } + + // We assume all other packets are ZRTP packets here. Process + // if ZRTP processing is enabled. Because valid RTP packets are + // already handled we delete any packets here after processing. + if (enableZrtp && zrtpEngine != NULL) { + // Fixed header length + smallest ZRTP packet (includes CRC) + if ((unsigned)rtn < (12 + sizeof(HelloAckPacket_t))) // data too small, dismiss + return 0; + + // Get CRC value into crc (see above how to compute the offset) + uint16_t temp = rtn - CRC_SIZE; + uint32_t crc = *(uint32_t*)(buffer + temp); + crc = ntohl(crc); + + if (!zrtpCheckCksum(buffer, temp, crc)) { + delete buffer; + if (zrtpUserCallback != NULL) + zrtpUserCallback->showMessage(Warning, WarningCRCmismatch); + return 0; + } + + packet = new IncomingZRTPPkt(buffer,rtn); + + uint32 magic = packet->getZrtpMagic(); + + // Check if it is really a ZRTP packet, if not delete it and return 0 + if (magic != ZRTP_MAGIC || zrtpEngine == NULL) { + delete packet; + return 0; + } + // cover the case if the other party sends _only_ ZRTP packets at the + // beginning of a session. Start ZRTP in this case as well. + if (!started) { + startZrtp(); + } + // this now points beyond the undefined and length field. + // We need them, thus adjust + unsigned char* extHeader = + const_cast<unsigned char*>(packet->getHdrExtContent()); + extHeader -= 4; + + // store peer's SSRC, used when creating the CryptoContext + peerSSRC = packet->getSSRC(); + zrtpEngine->processZrtpMessage(extHeader, peerSSRC); + } + delete packet; + return 0; +} + +size_t +ZrtpQueue::rtpDataPacket(unsigned char* buffer, int32 rtn, InetHostAddress network_address, tpport_t transport_port) +{ + // Special handling of padding to take care of encrypted content. + // In case of SRTP the padding length field is also encrypted, thus + // it gives a wrong length. Check and clear padding bit before + // creating the RTPPacket. Will be set and re-computed after a possible + // SRTP decryption. + uint8 padSet = (*buffer & 0x20); + if (padSet) { + *buffer = *buffer & ~0x20; // clear padding bit + } + // build a packet. It will link itself to its source + IncomingRTPPkt* packet = + new IncomingRTPPkt(buffer,rtn); + + // Generic header validity check. + if ( !packet->isHeaderValid() ) { + delete packet; + return 0; + } + + // Look for a CryptoContext for this packet's SSRC + CryptoContext* pcc = getInQueueCryptoContext(packet->getSSRC()); + + // If no crypto context is available for this SSRC but we are already in + // Secure state then create a CryptoContext for this SSRC. + // Assumption: every SSRC stream sent via this connection is secured + // _and_ uses the same crypto parameters. + if (pcc == NULL) { + pcc = getInQueueCryptoContext(0); + if (pcc != NULL) { + pcc = pcc->newCryptoContextForSSRC(packet->getSSRC(), 0, 0L); + if (pcc != NULL) { + pcc->deriveSrtpKeys(0); + setInQueueCryptoContext(pcc); + } + } + } + // If no crypto context: then either ZRTP is off or in early state + // If crypto context is available then unprotect data here. If an error + // occurs report the error and discard the packet. + if (pcc != NULL) { + int32 ret; + if ((ret = packet->unprotect(pcc)) < 0) { + if (!onSRTPPacketError(*packet, ret)) { + delete packet; + return 0; + } + } + if (started && zrtpEngine->inState(WaitConfAck)) { + zrtpEngine->conf2AckSecure(); + } + } + + // virtual for profile-specific validation and processing. + if (!onRTPPacketRecv(*packet) ) { + delete packet; + return 0; + } + if (padSet) { + packet->reComputePayLength(true); + } + // get time of arrival + struct timeval recvtime; + gettimeofday(&recvtime,NULL); + + bool source_created; + SyncSourceLink* sourceLink = + getSourceBySSRC(packet->getSSRC(),source_created); + SyncSource* s = sourceLink->getSource(); + if ( source_created ) { + // Set data transport address. + setDataTransportPort(*s,transport_port); + // Network address is assumed to be the same as the control one + setNetworkAddress(*s,network_address); + sourceLink->initStats(); + // First packet arrival time. + sourceLink->setInitialDataTime(recvtime); + sourceLink->setProbation(getMinValidPacketSequence()); + if ( sourceLink->getHello() ) + onNewSyncSource(*s); + } + else if ( 0 == s->getDataTransportPort() ) { + // Test if RTCP packets had been received but this is the + // first data packet from this source. + setDataTransportPort(*s,transport_port); + } + + // Before inserting in the queue, + // 1) check for collisions and loops. If the packet cannot be + // assigned to a source, it will be rejected. + // 2) check the source is a sufficiently well known source + // TODO: also check CSRC identifiers. + if (checkSSRCInIncomingRTPPkt(*sourceLink, source_created, + network_address, transport_port) && + recordReception(*sourceLink,*packet,recvtime) ) { + // now the packet link is linked in the queues + IncomingRTPPktLink* packetLink = new IncomingRTPPktLink(packet, sourceLink, recvtime, + packet->getTimestamp() - sourceLink->getInitialDataTimestamp(), + NULL,NULL,NULL,NULL); + insertRecvPacket(packetLink); + } else { + // must be discarded due to collision or loop or + // invalid source + delete packet; + return 0; + } + // Start the ZRTP engine after we got a at least one RTP packet and + // sent some as well or we are in multi-stream mode. + if (!started && enableZrtp) { + startZrtp(); + } + return rtn; +} + +bool +ZrtpQueue::onSRTPPacketError(IncomingRTPPkt& pkt, int32 errorCode) +{ + if (errorCode == -1) { + sendInfo(Warning, WarningSRTPauthError); + } + else { + sendInfo(Warning, WarningSRTPreplayError); + } + return false; +} + + +void +ZrtpQueue::putData(uint32 stamp, const unsigned char* data, size_t len) +{ + OutgoingDataQueue::putData(stamp, data, len); +} + + +void +ZrtpQueue::sendImmediate(uint32 stamp, const unsigned char* data, size_t len) +{ + OutgoingDataQueue::sendImmediate(stamp, data, len); +} + + +/* + * Here the callback methods required by the ZRTP implementation + */ +int32_t ZrtpQueue::sendDataZRTP(const unsigned char *data, int32_t length) { + + OutgoingZRTPPkt* packet = new OutgoingZRTPPkt(data, length); + + packet->setSSRC(getLocalSSRC()); + + packet->setSeqNum(senderZrtpSeqNo++); + + /* + * Compute the ZRTP CRC over the full ZRTP packet. Thus include + * the fixed packet header into the calculation. + */ + uint16_t temp = packet->getRawPacketSize() - CRC_SIZE; + uint8_t* pt = (uint8_t*)packet->getRawPacket(); + uint32_t crc = zrtpGenerateCksum(pt, temp); + // convert and store CRC in crc field of ZRTP packet. + crc = zrtpEndCksum(crc); + + // advance pointer to CRC storage + pt += temp; + *(uint32_t*)pt = htonl(crc); + + dispatchImmediate(packet); + delete packet; + + return 1; +} + +bool ZrtpQueue::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part) +{ + CryptoContext* recvCryptoContext; + CryptoContext* senderCryptoContext; + CryptoContextCtrl* recvCryptoContextCtrl; + CryptoContextCtrl* senderCryptoContextCtrl; + + int cipher; + int authn; + int authKeyLen; + + if (secrets->authAlgorithm == Sha1) { + authn = SrtpAuthenticationSha1Hmac; + authKeyLen = 20; + } + + if (secrets->authAlgorithm == Skein) { + authn = SrtpAuthenticationSkeinHmac; + authKeyLen = 32; + } + + if (secrets->symEncAlgorithm == Aes) + cipher = SrtpEncryptionAESCM; + + if (secrets->symEncAlgorithm == TwoFish) + cipher = SrtpEncryptionTWOCM; + + if (part == ForSender) { + // To encrypt packets: intiator uses initiator keys, + // responder uses responder keys + // Create a "half baked" crypto context first and store it. This is + // the main crypto context for the sending part of the connection. + if (secrets->role == Initiator) { + senderCryptoContext = new CryptoContext( + 0, + 0, + 0L, // keyderivation << 48, + cipher, // encryption algo + authn, // authtentication algo + (unsigned char*)secrets->keyInitiator, // Master Key + secrets->initKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltInitiator, // Master Salt + secrets->initSaltLen / 8, // Master Salt length + secrets->initKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->initSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag lenA + senderCryptoContextCtrl = new CryptoContextCtrl(0, + cipher, // encryption algo + authn, // authtication algo + (unsigned char*)secrets->keyInitiator, // Master Key + secrets->initKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltInitiator, // Master Salt + secrets->initSaltLen / 8, // Master Salt length + secrets->initKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->initSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + } + else { + senderCryptoContext = new CryptoContext( + 0, + 0, + 0L, // keyderivation << 48, + cipher, // encryption algo + authn, // authtentication algo + (unsigned char*)secrets->keyResponder, // Master Key + secrets->respKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltResponder, // Master Salt + secrets->respSaltLen / 8, // Master Salt length + secrets->respKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->respSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + senderCryptoContextCtrl = new CryptoContextCtrl(0, + cipher, // encryption algo + authn, // authtication algo + (unsigned char*)secrets->keyResponder, // Master Key + secrets->respKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltResponder, // Master Salt + secrets->respSaltLen / 8, // Master Salt length + secrets->respKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->respSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + } + if (senderCryptoContext == NULL) { + return false; + } + // Insert the Crypto templates (SSRC == 0) into the queue. When we send + // the first RTP or RTCP packet the real crypto context will be created. + // Refer to putData(), sendImmediate() in ccrtp's outqueue.cpp and + // takeinControlPacket() in ccrtp's control.cpp. + // + setOutQueueCryptoContext(senderCryptoContext); + setOutQueueCryptoContextCtrl(senderCryptoContextCtrl); + } + if (part == ForReceiver) { + // To decrypt packets: intiator uses responder keys, + // responder initiator keys + // See comment above. + if (secrets->role == Initiator) { + recvCryptoContext = new CryptoContext( + 0, + 0, + 0L, // keyderivation << 48, + cipher, // encryption algo + authn, // authtentication algo + (unsigned char*)secrets->keyResponder, // Master Key + secrets->respKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltResponder, // Master Salt + secrets->respSaltLen / 8, // Master Salt length + secrets->respKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->respSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + recvCryptoContextCtrl = new CryptoContextCtrl(0, + cipher, // encryption algo + authn, // authtication algo + (unsigned char*)secrets->keyResponder, // Master Key + secrets->respKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltResponder, // Master Salt + secrets->respSaltLen / 8, // Master Salt length + secrets->respKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->respSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + + } + else { + recvCryptoContext = new CryptoContext( + 0, + 0, + 0L, // keyderivation << 48, + cipher, // encryption algo + authn, // authtentication algo + (unsigned char*)secrets->keyInitiator, // Master Key + secrets->initKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltInitiator, // Master Salt + secrets->initSaltLen / 8, // Master Salt length + secrets->initKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->initSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + recvCryptoContextCtrl = new CryptoContextCtrl(0, + cipher, // encryption algo + authn, // authtication algo + (unsigned char*)secrets->keyInitiator, // Master Key + secrets->initKeyLen / 8, // Master Key length + (unsigned char*)secrets->saltInitiator, // Master Salt + secrets->initSaltLen / 8, // Master Salt length + secrets->initKeyLen / 8, // encryption keyl + authKeyLen, // authentication key len + secrets->initSaltLen / 8, // session salt len + secrets->srtpAuthTagLen / 8); // authentication tag len + } + if (recvCryptoContext == NULL) { + return false; + } + // Insert the Crypto templates (SSRC == 0) into the queue. When we receive + // the first RTP or RTCP packet the real crypto context will be created. + // Refer to rtpDataPacket() above and takeinControlPacket in ccrtp's control.cpp. + // + setInQueueCryptoContext(recvCryptoContext); + setInQueueCryptoContextCtrl(recvCryptoContextCtrl); + } + return true; +} + +void ZrtpQueue::srtpSecretsOn(std::string c, std::string s, bool verified) +{ + + if (zrtpUserCallback != NULL) { + zrtpUserCallback->secureOn(c); + if (!s.empty()) { + zrtpUserCallback->showSAS(s, verified); + } + } +} + +void ZrtpQueue::srtpSecretsOff(EnableSecurity part) { + if (part == ForSender) { + removeOutQueueCryptoContext(NULL); + removeOutQueueCryptoContextCtrl(NULL); + } + if (part == ForReceiver) { + removeInQueueCryptoContext(NULL); + removeInQueueCryptoContextCtrl(NULL); + } + if (zrtpUserCallback != NULL) { + zrtpUserCallback->secureOff(); + } +} + +int32_t ZrtpQueue::activateTimer(int32_t time) { + std::string s("ZRTP"); + if (staticTimeoutProvider != NULL) { + staticTimeoutProvider->requestTimeout(time, this, s); + } + return 1; +} + +int32_t ZrtpQueue::cancelTimer() { + std::string s("ZRTP"); + if (staticTimeoutProvider != NULL) { + staticTimeoutProvider->cancelRequest(this, s); + } + return 1; +} + +void ZrtpQueue::handleTimeout(const std::string &c) { + if (zrtpEngine != NULL) { + zrtpEngine->processTimeout(); + } +} + +void ZrtpQueue::handleGoClear() +{ + fprintf(stderr, "Need to process a GoClear message!"); +} + +void ZrtpQueue::sendInfo(MessageSeverity severity, int32_t subCode) { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->showMessage(severity, subCode); + } +} + +void ZrtpQueue::zrtpNegotiationFailed(MessageSeverity severity, int32_t subCode) { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->zrtpNegotiationFailed(severity, subCode); + } +} + +void ZrtpQueue::zrtpNotSuppOther() { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->zrtpNotSuppOther(); + } +} + +void ZrtpQueue::synchEnter() { + synchLock.enter(); +} + +void ZrtpQueue::synchLeave() { + synchLock.leave(); +} + +void ZrtpQueue::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->zrtpAskEnrollment(info); + } +} + +void ZrtpQueue::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->zrtpInformEnrollment(info); + } +} + +void ZrtpQueue::signSAS(uint8_t* sasHash) { + if (zrtpUserCallback != NULL) { + zrtpUserCallback->signSAS(sasHash); + } +} + +bool ZrtpQueue::checkSASSignature(uint8_t* sasHash) { + if (zrtpUserCallback != NULL) { + return zrtpUserCallback->checkSASSignature(sasHash); + } + return false; +} + +void ZrtpQueue::setEnableZrtp(bool onOff) { + enableZrtp = onOff; +} + +bool ZrtpQueue::isEnableZrtp() { + return enableZrtp; +} + +void ZrtpQueue::SASVerified() { + if (zrtpEngine != NULL) + zrtpEngine->SASVerified(); +} + +void ZrtpQueue::resetSASVerified() { + if (zrtpEngine != NULL) + zrtpEngine->resetSASVerified(); +} + +void ZrtpQueue::goClearOk() { } + +void ZrtpQueue::requestGoClear() { } + +void ZrtpQueue::setAuxSecret(uint8* data, int32_t length) { + if (zrtpEngine != NULL) + zrtpEngine->setAuxSecret(data, length); +} + +void ZrtpQueue::setUserCallback(ZrtpUserCallback* ucb) { + zrtpUserCallback = ucb; +} + +void ZrtpQueue::setClientId(std::string id) { + clientIdString = id; +} + +std::string ZrtpQueue::getHelloHash() { + if (zrtpEngine != NULL) + return zrtpEngine->getHelloHash(); + else + return std::string(); +} + +std::string ZrtpQueue::getPeerHelloHash() { + if (zrtpEngine != NULL) + return zrtpEngine->getPeerHelloHash(); + else + return std::string(); +} + +std::string ZrtpQueue::getMultiStrParams() { + if (zrtpEngine != NULL) + return zrtpEngine->getMultiStrParams(); + else + return std::string(); +} + +void ZrtpQueue::setMultiStrParams(std::string parameters) { + if (zrtpEngine != NULL) + zrtpEngine->setMultiStrParams(parameters); +} + +bool ZrtpQueue::isMultiStream() { + if (zrtpEngine != NULL) + return zrtpEngine->isMultiStream(); + return false; +} + +bool ZrtpQueue::isMultiStreamAvailable() { + if (zrtpEngine != NULL) + return zrtpEngine->isMultiStreamAvailable(); + return false; +} + +void ZrtpQueue::acceptEnrollment(bool accepted) { + if (zrtpEngine != NULL) + zrtpEngine->acceptEnrollment(accepted); +} + +std::string ZrtpQueue::getSasType() { + if (zrtpEngine != NULL) + return zrtpEngine->getSasType(); + else + return NULL; +} + +uint8_t* ZrtpQueue::getSasHash() { + if (zrtpEngine != NULL) + return zrtpEngine->getSasHash(); + else + return NULL; +} + +bool ZrtpQueue::sendSASRelayPacket(uint8_t* sh, std::string render) { + + if (zrtpEngine != NULL) + return zrtpEngine->sendSASRelayPacket(sh, render); + else + return false; +} + +bool ZrtpQueue::isMitmMode() { + return mitmMode; +} + +void ZrtpQueue::setMitmMode(bool mitmMode) { + this->mitmMode = mitmMode; +} + +bool ZrtpQueue::isEnrollmentMode() { + if (zrtpEngine != NULL) + return zrtpEngine->isEnrollmentMode(); + else + return false; +} + +void ZrtpQueue::setEnrollmentMode(bool enrollmentMode) { + if (zrtpEngine != NULL) + zrtpEngine->setEnrollmentMode(enrollmentMode); +} + +void ZrtpQueue::setParanoidMode(bool yesNo) { + enableParanoidMode = yesNo; +} + +bool ZrtpQueue::isParanoidMode() { + return enableParanoidMode; +} + +bool ZrtpQueue::isPeerEnrolled() { + if (zrtpEngine != NULL) + return zrtpEngine->isPeerEnrolled(); + else + return false; +} + +void ZrtpQueue::setSignSas(bool sasSignMode) { + signSas = sasSignMode; +} + +bool ZrtpQueue::setSignatureData(uint8* data, int32 length) { + if (zrtpEngine != NULL) + return zrtpEngine->setSignatureData(data, length); + return 0; +} + +const uint8* ZrtpQueue::getSignatureData() { + if (zrtpEngine != NULL) + return zrtpEngine->getSignatureData(); + return 0; +} + +int32 ZrtpQueue::getSignatureLength() { + if (zrtpEngine != NULL) + return zrtpEngine->getSignatureLength(); + return 0; +} + +int32 ZrtpQueue::getPeerZid(uint8* data) { + if (data == NULL) + return 0; + + if (zrtpEngine != NULL) + return zrtpEngine->getPeerZid(data); + + return 0; +} + +IncomingZRTPPkt::IncomingZRTPPkt(const unsigned char* const block, size_t len) : + IncomingRTPPkt(block,len) { +} + +uint32 IncomingZRTPPkt::getZrtpMagic() const { + return ntohl(getHeader()->timestamp); +} + +uint32 IncomingZRTPPkt::getSSRC() const { + return ntohl(getHeader()->sources[0]); +} + +OutgoingZRTPPkt::OutgoingZRTPPkt( + const unsigned char* const hdrext, uint32 hdrextlen) : + OutgoingRTPPkt(NULL, 0, hdrext, hdrextlen, NULL ,0, 0, NULL) +{ + getHeader()->version = 0; + getHeader()->timestamp = htonl(ZRTP_MAGIC); +} + +END_NAMESPACE + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/src/ZrtpStateClass.cpp b/src/ZrtpStateClass.cpp new file mode 100644 index 0000000..cff9c6e --- /dev/null +++ b/src/ZrtpStateClass.cpp @@ -0,0 +1,1473 @@ +/* + Copyright (C) 2006-2008 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <iostream> +#include <cstdlib> +#include <ctype.h> + +#include <libzrtpcpp/ZRtp.h> +#include <libzrtpcpp/ZrtpStateClass.h> + +using namespace std; +using namespace GnuZrtpCodes; + +state_t states[numberOfStates] = { + {Initial, &ZrtpStateClass::evInitial }, + {Detect, &ZrtpStateClass::evDetect }, + {AckDetected, &ZrtpStateClass::evAckDetected }, + {AckSent, &ZrtpStateClass::evAckSent }, + {WaitCommit, &ZrtpStateClass::evWaitCommit }, + {CommitSent, &ZrtpStateClass::evCommitSent }, + {WaitDHPart2, &ZrtpStateClass::evWaitDHPart2 }, + {WaitConfirm1, &ZrtpStateClass::evWaitConfirm1 }, + {WaitConfirm2, &ZrtpStateClass::evWaitConfirm2 }, + {WaitConfAck, &ZrtpStateClass::evWaitConfAck }, + {WaitClearAck, &ZrtpStateClass::evWaitClearAck }, + {SecureState, &ZrtpStateClass::evSecureState }, + {WaitErrorAck, &ZrtpStateClass::evWaitErrorAck } +}; + + +ZrtpStateClass::ZrtpStateClass(ZRtp *p) { + parent = p; + secSubstate = Normal; + engine = new ZrtpStates(states, numberOfStates, Initial); + + commitPkt = NULL; + multiStream = false; + + // Set up timers according to ZRTP spec + T1.start = 50; + T1.maxResend = 20; + T1.capping = 200; + + T2.start = 150; + T2.maxResend = 10; + T2.capping = 600; +} + +ZrtpStateClass::~ZrtpStateClass(void) { + + // If not in Initial state: close the protocol engine + // before destroying it. This will free pending packets + // if necessary. + if (!inState(Initial)) { + Event_t ev; + + cancelTimer(); + ev.type = ZrtpClose; + event = &ev; + engine->processEvent(*this); + } + delete engine; +} + +void ZrtpStateClass::processEvent(Event_t *ev) { + + event = ev; + char *msg, first, middle, last; + uint8_t *pkt; + + parent->synchEnter(); + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + first = tolower(*msg); + middle = tolower(*(msg+4)); + last = tolower(*(msg+7)); + + // Check if this is an Error packet. + if (first == 'e' && middle =='r' && last == ' ') { + /* + * Process a received Error packet. + * + * In any case stop timer to prevent resending packets. + * Use callback method to prepare and get an ErrorAck packet. + * Modify event type to "ErrorPkt" and hand it over to current + * state for further processing. + */ + cancelTimer(); + ZrtpPacketError epkt(pkt); + ZrtpPacketErrorAck* eapkt = parent->prepareErrorAck(&epkt); + parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(eapkt)); + event->type = ErrorPkt; + } + else if (first == 'p' && middle == ' ' && last == ' ') { + ZrtpPacketPing ppkt(pkt); + ZrtpPacketPingAck* ppktAck = parent->preparePingAck(&ppkt); + if (ppktAck != NULL) { // ACK only to valid PING packet, otherwise ignore it + parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(ppktAck)); + } + parent->synchLeave(); + return; + } + else if (first == 's' && last == 'y') { + uint32_t errorCode = 0; + ZrtpPacketSASrelay* srly = new ZrtpPacketSASrelay(pkt); + ZrtpPacketRelayAck* rapkt = parent->prepareRelayAck(srly, &errorCode); + parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(rapkt)); + parent->synchLeave(); + return; + } + + } + /* + * Shut down protocol state engine: cancel outstanding timer, further + * processing in current state. + */ + else if (event->type == ZrtpClose) { + cancelTimer(); + } + engine->processEvent(*this); + parent->synchLeave(); +} + + +void ZrtpStateClass::evInitial(void) { + DEBUGOUT((cout << "Checking for match in Initial.\n")); + + if (event->type == ZrtpInitial) { + ZrtpPacketHello* hello = parent->prepareHello(); + + // remember packet for easy resend in case timer triggers + sentPacket = static_cast<ZrtpPacketBase *>(hello); + + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T1) <= 0) { + timerFailed(SevereNoTimer); // returns to state Initial + return; + } + nextState(Detect); + } +} + +/* + * Detect state. + * + * When in this state the protocol engine sent an initial Hello packet + * to the peer. + * + * When entering this state transition function then: + * - Assume Initiator mode, mode may change later on peer reaction + * - Instance variable sentPacket contains the sent Hello packet + * - Hello timer T1 may be active. This is the case if the other peer + * has prepared its RTP session and answers our Hello packets nearly + * immediately, i.e. before the Hello timeout counter expires. If the + * other peer does not send a Hello during this time the state engine + * reports "other peer does not support ZRTP" but stays + * in state Detect with no active timer (passiv mode). Staying in state + * Detect allows another peer to start its detect phase any time later. + * + * This restart capability is the reason why we use "startTimer(&T1)" in + * case we received a Hello packet from another peer. This effectively + * restarts the Hello timeout counter. + * + * In this state we also handle ZrtpInitialize event. This forces a + * restart of ZRTP discovery if an application calls ZrtpQueue#startZrtp + * again. This may happen after a previous discovery phase were not + * successful. + * + * Usually applications use some sort of signaling protocol, for example + * SIP, to negotiate the RTP parameters. Thus the RTP sessions setup is + * fairly sychronized and thus also the ZRTP detection phase. Applications + * that use some other ways to setup the RTP sessions this restart capability + * comes in handy because no RTP setup sychronization is necessary. + * + * Possible events in this state are: + * - timeout for sent Hello packet: causes a resend check and + * repeat sending of Hello packet + * - received a HelloAck: stop active timer, prepare and send Hello packet, + * switch to state AckDeteced. + * - received a Hello: stop active timer, send HelloAck, prepare Commit + * packet, switch to state AckSent. + * + */ +void ZrtpStateClass::evDetect(void) { + + DEBUGOUT((cout << "Checking for match in Detect.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + /* + * First check the general event type, then discrimnate + * the real event. + */ + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + /* + * HelloAck: + * - our peer acknowledged our Hello packet, we have not seen the peer's Hello yet + * - cancel timer T1 to stop resending Hello + * - switch to state AckDetected, wait for peer's Hello (F3) + */ + if (first == 'h' && last =='k') { + cancelTimer(); + sentPacket = NULL; + nextState(AckDetected); + return; + } + /* + * Hello: + * - send HelloAck packet to acknowledge the received Hello packet + * - use received Hello packet to prepare own Commit packet. We need to + * do it at this point because we need the hash value computed from + * peer's Hello packet. Follwing states my use the prepared Commit. + * - switch to new state AckSent which sends own Hello packet until + * peer acknowledges this + * - Don't clear sentPacket, points to Hello + */ + if (first == 'h' && last ==' ') { + cancelTimer(); + ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck(); + + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { + parent->zrtpNegotiationFailed(Severe, SevereCannotSend); + return; + } + // Use peer's Hello packet to create my commit packet, store it + // for possible later usage in state AckSent + ZrtpPacketHello hpkt(pkt); + commitPkt = parent->prepareCommit(&hpkt, &errorCode); + + nextState(AckSent); + if (commitPkt == NULL) { + sendErrorPacket(errorCode); // switches to Error state + return; + } + if (startTimer(&T1) <= 0) { // restart own Hello timer/counter + timerFailed(SevereNoTimer); // returns to state Initial + } + T1.maxResend = 60; // more retries to extend time, see chap. 6 + } + return; // unknown packet for this state - Just ignore it + } + // Timer event triggered - this is Timer T1 to resend Hello + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (nextTimer(&T1) <= 0) { + commitPkt = NULL; + parent->zrtpNotSuppOther(); + nextState(Detect); + } + } + // If application call zrtpStart() to restart discovery + else if (event->type == ZrtpInitial) { + cancelTimer(); + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T1) <= 0) { + timerFailed(SevereNoTimer); // returns to state Initial + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * AckSent state. + * + * The protocol engine got a Hello packet from peer and answered with a + * HelloAck response. According to the protocol we must also send a + * Hello after HelloAck (refer to figure 1 in ZRTP RFC xxxx, message + * HelloACK (F2) must be followed by Hello (F3)). We use the timeout in + * this state to send the required Hello (F3). + * + * Our peer must acknowledge the Hello with HelloAck. In earlier versions + * also a Commit was a valid packet thus the code covers this. + * Figure 1 in the RFC shows the HelloAck, chapter 7 states that a Commit + * may be send to acknowledge Hello. There is one constraint when using a Commit to + * acknowledge Hello: refer to chapter 4.1 that requires that both parties + * have completed the Hello/HelloAck discovery handshake. This implies that + * only message F4 may be replaced by a Commit. This constraint guarantees + * that both peers have seen at least one Hello. + * + * When entering this transition function: + * - The instance variabe sentPacket contains own Hello packet + * - The instance variabe commitPkt points to prepared Commit packet + * - Timer T1 is active + * + * Possible events in this state are: + * - timeout for sent Hello packet: causes a resend check and repeat sending + * of Hello packet + * - HelloAck: The peer answered with HelloAck to own HelloAck/Hello. Send + * prepared Commit packet and try Initiator mode. + * - Commit: The peer answered with Commit to HelloAck/Hello, thus switch to + * responder mode. + * - Hello: If the protcol engine receives another Hello it repeats the + * HelloAck/Hello response until Timer T1 exceeds its maximum. This may + * happen if the other peer sends Hello only (maybe due to network problems) + */ +void ZrtpStateClass::evAckSent(void) { + + DEBUGOUT((cout << "Checking for match in AckSent.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + /* + * First check the general event type, then discrimnate + * the real event. + */ + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * HelloAck: + * The peer answers with HelloAck to own HelloAck/Hello. Send Commit + * and try Initiator mode. The requirement defined in chapter 4.1 to + * have a complete Hello/HelloAck is fulfilled. + * - stop Hello timer T1 + * - send own Commit message + * - switch state to CommitSent, start Commit timer, assume Initiator + */ + if (first == 'h' && last =='k') { + cancelTimer(); + + // remember packet for easy resend in case timer triggers + // Timer trigger received in new state CommitSend + sentPacket = static_cast<ZrtpPacketBase *>(commitPkt); + commitPkt = NULL; // now stored in sentPacket + nextState(CommitSent); + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); // returns to state Initial + } + return; + } + /* + * Hello: + * - peer didn't receive our HelloAck + * - repeat HelloAck/Hello response: + * -- get HelloAck packet, send it + * -- The timeout trigger of T1 sends our Hello packet + * -- stay in state AckSent + * + * Similar to Detect state: just acknowledge the Hello, the next + * timeout sends the following Hello. + */ + + if (first == 'h' && last ==' ') { + ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck(); + + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { + nextState(Detect); + parent->zrtpNegotiationFailed(Severe, SevereCannotSend); + } + return; + } + /* + * Commit: + * The peer answers with Commit to HelloAck/Hello, thus switch to + * responder mode. + * - stop timer T1 + * - prepare and send our DHPart1 + * - switch to state WaitDHPart2 and wait for peer's DHPart2 + * - don't start timer, we are responder + */ + if (first == 'c') { + cancelTimer(); + ZrtpPacketCommit cpkt(pkt); + + if (!multiStream) { + ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode); + + // Something went wrong during processing of the Commit packet + if (dhPart1 == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + commitPkt = NULL; + sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); + nextState(WaitDHPart2); + } + else { + ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode); + + // Something went wrong during processing of the Commit packet + if (confirm == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + nextState(WaitConfirm2); + } + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + } + } + /* + * Timer: + * - resend Hello packet, stay in state, restart timer until repeat + * counter triggers + * - if repeat counter triggers switch to state Detect, con't clear + * sentPacket, Detect requires it to point to own Hello message + */ + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + return sendFailed(); // returns to state Initial + } + if (nextTimer(&T1) <= 0) { + parent->zrtpNotSuppOther(); + commitPkt = NULL; + // Stay in state Detect to be prepared get an hello from + // other peer any time later + nextState(Detect); + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + commitPkt = NULL; + sentPacket = NULL; + nextState(Initial); + } +} +/* + * AckDetected state. + * + * The protocol engine received a HelloAck in state Detect, thus the peer + * acknowledged our the Hello. According to ZRT RFC xxxx our peer must send + * its Hello until our protocol engine sees it (refer also to comment for + * state AckSent). This protocol sequence gurantees that both peers got at + * least one Hello. + * + * When entering this transition function + * - instance variable sentPacket is NULL, Hello timer stopped + * + * Possible events in this state are: + * Hello: we have to choices + * 1) we can acknowledge the peer's Hello with a HelloAck + * 2) we can acknowledge the peer's Hello with a Commit + * Both choices are implemented and may be enabled by setting a compile + * time #if (see code below). Currently we use choice 1) here because + * it's more aligned to the ZRTP specification + */ +void ZrtpStateClass::evAckDetected(void) { + + DEBUGOUT((cout << "Checking for match in AckDetected.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + +#if 1 + /* + * Implementation for choice 1) + * Hello: + * - Acknowledge peers Hello, sending HelloACK (F4) + * - switch to state WaitCommit, wait for peer's Commit + * - we are going to be in the Responder role + */ + + if (first == 'h' && last ==' ') { + // Parse Hello packet and build an own Commit packet even if the + // Commit is not send to the peer. We need to do this to check the + // Hello packet and prepare the shared secret stuff. + ZrtpPacketHello hpkt(pkt); + ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode); + + // Something went wrong during processing of the Hello packet, for + // example wrong version, duplicate ZID. + if (commit == NULL) { + sendErrorPacket(errorCode); + return; + } + ZrtpPacketHelloAck *helloAck = parent->prepareHelloAck(); + nextState(WaitCommit); + + // remember packet for easy resend + sentPacket = static_cast<ZrtpPacketBase *>(helloAck); + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { + sendFailed(); + } + } +#else + /* + * Implementation for choice 2) + * Hello: + * - Acknowledge peers Hello by sending Commit (F5) + * instead of HelloAck (F4) + * - switch to state CommitSent + * - Initiator role, thus start timer T2 to monitor timeout for Commit + */ + + if (first == 'h') { + // Parse peer's packet data into a Hello packet + ZrtpPacketHello hpkt(pkt); + ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode); + // Something went wrong during processing of the Hello packet + if (commit == NULL) { + sendErrorPacket(errorCode); + return; + } + nextState(CommitSent); + + // remember packet for easy resend in case timer triggers + // Timer trigger received in new state CommitSend + sentPacket = static_cast<ZrtpPacketBase *>(commit); + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); + return; + } + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); + } + } +#endif + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + nextState(Initial); + } +} + +/* + * WaitCommit state. + * + * This state is only used if we use choice 1) in AckDetected. + * + * When entering this transition function + * - instance variable sentPacket contains a HelloAck packet + * + * Possible events in this state are: + * - Hello: just resend our HelloAck + * - Commit: prepare and send our DHPart1 message to start first + * half of DH key agreement. Switch to state WaitDHPart2, don't + * start any timer, we a Responder. + */ +void ZrtpStateClass::evWaitCommit(void) { + + DEBUGOUT((cout << "Checking for match in WaitCommit.\n")); + + char *msg, first; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + /* + * Hello: + * - resend HelloAck + * - stay in WaitCommit + */ + if (first == 'h') { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + return; + } + /* + * Commit: + * - prepare DH1Part packet or Confirm1 if multi stream mode + * - send it to peer + * - switch state to WaitDHPart2 or WaitConfirm2 if multi stream mode + * - don't start timer, we are responder + */ + if (first == 'c') { + ZrtpPacketCommit cpkt(pkt); + + if (!multiStream) { + ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode); + + // Something went wrong during processing of the Commit packet + if (dhPart1 == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); + nextState(WaitDHPart2); + } + else { + ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode); + + // Something went wrong during processing of the Commit packet + if (confirm == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + nextState(WaitConfirm2); + } + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * CommitSent state. + * + * This state either handles a DH1Part1 message to start the first + * half of DH key agreement or it handles a Commit clash. If handling a + * Commit clash it may happen that we change our role from Initiator to + * Responder. + * + * When entering this transition function + * - assume Initiator mode, may change if we reveice a Commit here + * - sentPacket contains Commit packet + * - Commit timer (T2) active + * + * Possible events in this state are: + * - timeout for sent Commit packet: causes a resend check and repeat sending + * of Commit packet + * - Commit: This is a Commit clash. Break the tie accroding to chapter 5.2 + * - DHPart1: start first half of DH key agreement. Perpare and send own DHPart2 + * and switch to state WaitConfirm1. + */ + +void ZrtpStateClass::evCommitSent(void) { + + DEBUGOUT((cout << "Checking for match in CommitSend.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * HelloAck or Hello: + * - delayed "HelloAck" or "Hello", maybe due to network latency, just + * ignore it + * - no switch in state, leave timer as it is + */ + if (first == 'h' && (last =='k' || last == ' ')) { + return; + } + + /* + * Commit: + * We have a "Commit" clash. Resolve it. + * + * - switch off resending Commit + * - compare my hvi with peer's hvi + * - if my hvi is greater + * - we are Initiator, stay in state, wait for peer's DHPart1 packet + * - else + * - we are Responder, stop timer + * - prepare and send DH1Packt, + * - switch to state WaitDHPart2, implies Responder path + */ + if (first == 'c' && last == ' ') { + ZrtpPacketCommit zpCo(pkt); + + if (!parent->verifyH2(&zpCo)) { + return; + } + cancelTimer(); // this cancels the Commit timer T2 + + // if our hvi is less than peer's hvi: switch to Responder mode and + // send DHPart1 or Confirm1 packet. Peer (as Initiator) will retrigger if + // necessary + // + if (parent->compareCommit(&zpCo) < 0) { + if (!multiStream) { + ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&zpCo, &errorCode); + + // Something went wrong during processing of the Commit packet + if (dhPart1 == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + nextState(WaitDHPart2); + sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); + } + else { + ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&zpCo, &errorCode); + + // Something went wrong during processing of the Commit packet + if (confirm == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + nextState(WaitConfirm2); + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + } + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + } + // Stay in state, we are Initiator, wait for DHPart1 of Confirm1 packet from peer. + // Resend Commit after timeout until we get a DHPart1 or Confirm1 + else { + if (startTimer(&T2) <= 0) { // restart the Commit timer, gives peer more time to react + timerFailed(SevereNoTimer); // returns to state Initial + } + } + return; + } + + /* + * DHPart1: + * - switch off resending Commit + * - Prepare and send DHPart2 + * - switch to WaitConfirm1 + * - start timer to resend DHPart2 if necessary, we are Initiator + */ + if (first == 'd') { + cancelTimer(); + sentPacket = NULL; + ZrtpPacketDHPart dpkt(pkt); + ZrtpPacketDHPart* dhPart2 = parent->prepareDHPart2(&dpkt, &errorCode); + + // Something went wrong during processing of the DHPart1 packet + if (dhPart2 == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + else { + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); // switches to state Initial + } + } + + return; + } + sentPacket = static_cast<ZrtpPacketBase *>(dhPart2); + nextState(WaitConfirm1); + + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); // switches to state Initial + } + return; + } + + if (multiStream && (first == 'c' && last == '1')) { + cancelTimer(); + ZrtpPacketConfirm cpkt(pkt); + + ZrtpPacketConfirm* confirm = parent->prepareConfirm2MultiStream(&cpkt, &errorCode); + + // Something went wrong during processing of the Confirm1 packet + if (confirm == NULL) { + sendErrorPacket(errorCode); + return; + } + nextState(WaitConfAck); + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); // returns to state Initial + return; + } + // according to chap 5.6: after sending Confirm2 the Initiator must + // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck + // state. + if (!parent->srtpSecretsReady(ForReceiver)) { + parent->sendInfo(Severe, CriticalSWError); + sendErrorPacket(CriticalSWError); + return; + } + } + } + // Timer event triggered, resend the Commit packet + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (nextTimer(&T2) <= 0) { + timerFailed(SevereTooMuchRetries); // returns to state Initial + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * WaitDHPart2 state. + * + * This state handles the second part of SH key agreement. Only the Resonder + * can enter this state. + * + * When entering this transition function + * - sentPacket contains DHPart1 packet, no timer active + * + * Possible events in this state are: + * - Commit: Our peer didn't receive out DHPart1 thus the peer sends Commit again. + * Just repeat our DHPart1. + * - DHPart2: start second half of DH key agreement. Perpare and send own Confirm1 + * and switch to state WaitConfirm2. + */ +void ZrtpStateClass::evWaitDHPart2(void) { + + DEBUGOUT((cout << "Checking for match in DHPart2.\n")); + + char *msg, first; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + /* + * Commit: + * - resend DHPart1 + * - stay in state + */ + if (first == 'c') { + if (!parent->sendPacketZRTP(sentPacket)) { + return sendFailed(); // returns to state Initial + } + return; + } + /* + * DHPart2: + * - prepare Confirm1 packet + * - switch to WaitConfirm2 + * - No timer, we are responder + */ + if (first == 'd') { + ZrtpPacketDHPart dpkt(pkt); + ZrtpPacketConfirm* confirm = parent->prepareConfirm1(&dpkt, &errorCode); + + if (confirm == NULL) { + if (errorCode != IgnorePacket) { + sendErrorPacket(errorCode); + } + return; + } + nextState(WaitConfirm2); + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * WaitConirm1 state. + * + * This state handles a received Confirm1 message and only the Initiator + * can enter this state. + * + * When entering this transition function in DH mode: + * - Initiator mode + * - sentPacket contains DHPart2 packet, DHPart2 timer active + * + * When entering this transition function in Multi stream mode via AckSent: + * - Initiator mode + * - sentPacket contains my Commit packet, Commit timer active + * +* Possible events in this state are: + * - timeout for sent DHPart2 packet: causes a resend check and repeat sending + * of DHPart2 packet. + * - Confirm1: Check Confirm1 message. If it is ok then prepare and send own + * Confirm2 packet and switch to state WaitConfAck. + */ +void ZrtpStateClass::evWaitConfirm1(void) { + + DEBUGOUT((cout << "Checking for match in WaitConfirm1.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * Confirm1: + * - Switch off resending DHPart2 + * - prepare a Confirm2 packet + * - switch to state WaitConfAck + * - set timer to monitor Confirm2 packet, we are initiator + */ + if (first == 'c' && last == '1') { + cancelTimer(); + ZrtpPacketConfirm cpkt(pkt); + + ZrtpPacketConfirm* confirm = parent->prepareConfirm2(&cpkt, &errorCode); + + // Something went wrong during processing of the Confirm1 packet + if (confirm == NULL) { + sendErrorPacket(errorCode); + return; + } + nextState(WaitConfAck); + sentPacket = static_cast<ZrtpPacketBase *>(confirm); + + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (startTimer(&T2) <= 0) { + timerFailed(SevereNoTimer); // returns to state Initial TODO check for return following this line + } + // according to chap 5.8: after sending Confirm2 the Initiator must + // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck + // state. + if (!parent->srtpSecretsReady(ForReceiver)) { + parent->sendInfo(Severe, CriticalSWError); + sendErrorPacket(CriticalSWError); + return; + } + } + } + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (nextTimer(&T2) <= 0) { + timerFailed(SevereTooMuchRetries); // returns to state Initial + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * WaitConfirm2 state. + * + * Handles the Confirm2 message that closes the key agreement handshake. Only + * the Responder can enter this state. If the Confirm2 message is ok send a + * Conf2Ack to our peer. Switch to secure mode after sending Conf2Ack, our + * peer switches to secure mode after receiving Conf2Ack. + * + * TODO - revise documentation comments + * + * When entering this transition function + * - Responder mode + * - sentPacket contains Confirm1 packet, no timer active + * + * Possible events in this state are: + * - DHPart2: Our peer didn't receive our Confirm1 thus sends DHPart2 again. + * Just repeat our Confirm1. + * - Confirm2: close DH key agreement. Perpare and send own Conf2Ack + * and switch to state SecureState. + */ +void ZrtpStateClass::evWaitConfirm2(void) { + + DEBUGOUT((cout << "Checking for match in WaitConfirm2.\n")); + + char *msg, first, last; + uint8_t *pkt; + uint32_t errorCode = 0; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * DHPart2 or Commit in multi stream mode: + * - resend Confirm1 packet + * - stay in state + */ + if (first == 'd' || (multiStream && (first == 'c' && last == ' '))) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + } + return; + } + /* + * Confirm2: + * - prepare ConfAck + * - switch on security (SRTP) + * - switch to SecureState + */ + if (first == 'c' && last == '2') { + ZrtpPacketConfirm cpkt(pkt); + ZrtpPacketConf2Ack* confack = parent->prepareConf2Ack(&cpkt, &errorCode); + + // Something went wrong during processing of the confirm2 packet + if (confack == NULL) { + sendErrorPacket(errorCode); + return; + } + sentPacket = static_cast<ZrtpPacketBase *>(confack); + + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (!parent->srtpSecretsReady(ForSender) || + !parent->srtpSecretsReady(ForReceiver)) { + parent->sendInfo(Severe, CriticalSWError); + sendErrorPacket(CriticalSWError); + return; + } + nextState(SecureState); + parent->sendInfo(Info, InfoSecureStateOn); + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +/* + * WaitConf2Ack state. + * + * This state handles the Conf2Ack message that acknowledges the successfull + * processing of Confirm2. Only the Initiator can enter this state. Switch on + * secure mode and switch to state SecureState. + * + * When entering this transition function + * - Initiator mode + * - sentPacket contains Confirm2 packet, Confirm2 timer active + * - receiver security switched on + * + * Possible events in this state are: + * - timeout for sent Confirm2 packet: causes a resend check and repeat sending + * of Confirm2 packet + * - Conf2Ack: Key agreement was successfull, switch to secure mode. + */ +void ZrtpStateClass::evWaitConfAck(void) { + + DEBUGOUT((cout << "Checking for match in WaitConfAck.\n")); + + char *msg, first; + uint8_t *pkt; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + /* + * ConfAck: + * - Switch off resending Confirm2 + * - switch to SecureState + */ + if (first == 'c') { + cancelTimer(); + sentPacket = NULL; + // Receiver was already enabled after sending Confirm2 packet + // see previous states. + if (!parent->srtpSecretsReady(ForSender)) { + parent->sendInfo(Severe, CriticalSWError); + sendErrorPacket(CriticalSWError); + return; + } + nextState(SecureState); + // TODO: call parent to clear signature data at initiator + parent->sendInfo(Info, InfoSecureStateOn); + } + } + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + parent->srtpSecretsOff(ForReceiver); + return; + } + if (nextTimer(&T2) <= 0) { + timerFailed(SevereTooMuchRetries); // returns to state Initial + parent->srtpSecretsOff(ForReceiver); + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + parent->srtpSecretsOff(ForReceiver); + } +} + +/* + * When entering this transition function + * - sentPacket contains GoClear packet, GoClear timer active + */ + +void ZrtpStateClass::evWaitClearAck(void) { + DEBUGOUT((cout << "Checking for match in ClearAck.\n")); + + char *msg, first, last; + uint8_t *pkt; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * ClearAck: + * - stop resending GoClear, + * - switch to state AckDetected, wait for peer's Hello + */ + if (first == 'c' && last =='k') { + cancelTimer(); + sentPacket = NULL; + nextState(Initial); + } + } + // Timer event triggered - this is Timer T2 to resend GoClear w/o HMAC + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (nextTimer(&T2) <= 0) { + timerFailed(SevereTooMuchRetries); // returns to state Initial + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + + +/* + * WaitErrorAck state. + * + * This state belongs to the "error handling state overlay" and handle + * ErrorAck message. Most of the ZRTP states can send Error message for + * example if they detect wrong packets. After sending an Error message + * the protocol engine switches to WaitErrorAck state. Receiving an + * ErrorAck message completes the ZRTP error handling. + * + * When entering this transition function + * - sentPacket contains Error packet, Error timer active + * + * Possible events in this state are: + * - timeout for sent Error packet: causes a resend check and repeat sending + * of Error packet + * - ErrorAck: Stop timer and switch to state Initial. + */ + +void ZrtpStateClass::evWaitErrorAck(void) { + DEBUGOUT((cout << "Checking for match in ErrorAck.\n")); + + char *msg, first, last; + uint8_t *pkt; + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * Errorck: + * - stop resending Error, + * - switch to state Initial + */ + if (first == 'e' && last =='k') { + cancelTimer(); + sentPacket = NULL; + nextState(Initial); + } + } + // Timer event triggered - this is Timer T2 to resend Error. + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return; + } + if (nextTimer(&T2) <= 0) { + timerFailed(SevereTooMuchRetries); // returns to state Initial + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + sentPacket = NULL; + nextState(Initial); + } +} + +void ZrtpStateClass::evSecureState(void) { + + DEBUGOUT((cout << "Checking for match in SecureState.\n")); + + char *msg, first, last; + uint8_t *pkt; + + /* + * Handle a possible substate. If substate handling was ok just return. + */ + if (secSubstate == WaitSasRelayAck) { + if (subEvWaitRelayAck()) + return; + } + + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * Confirm2: + * - resend Conf2Ack packet + * - stay in state + */ + if (first == 'c' && last == '2') { + if (sentPacket != NULL && !parent->sendPacketZRTP(sentPacket)) { + sentPacket = NULL; + nextState(Initial); + parent->srtpSecretsOff(ForSender); + parent->srtpSecretsOff(ForReceiver); + parent->zrtpNegotiationFailed(Severe, SevereCannotSend); + } + return; + } + /* + * GoClear received, handle it. TODO fix go clear handling + */ + if (first == 'g' && last == 'r') { + ZrtpPacketGoClear gpkt(pkt); + ZrtpPacketClearAck* clearAck = parent->prepareClearAck(&gpkt); + + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(clearAck))) { + return; + } + // TODO Timeout to resend clear ack until user user confirmation + } + } + else { // unknown Event type for this state (covers Error and ZrtpClose) + sentPacket = NULL; + parent->srtpSecretsOff(ForSender); + parent->srtpSecretsOff(ForReceiver); + nextState(Initial); + if (event->type != ZrtpClose) { + parent->zrtpNegotiationFailed(Severe, SevereProtocolError); + } + parent->sendInfo(Info, InfoSecureStateOff); + } +} + +bool ZrtpStateClass::subEvWaitRelayAck() { + char *msg, first, last; + uint8_t* pkt; + + /* + * First check the general event type, then discrimnate the real event. + */ + if (event->type == ZrtpPacket) { + pkt = event->packet; + msg = (char *)pkt + 4; + + first = tolower(*msg); + last = tolower(*(msg+7)); + + /* + * SAS relayAck: + * - stop resending SASRelay, + * - switch to secure substate Normal + */ + if (first == 'r' && last =='k') { + cancelTimer(); + secSubstate = Normal; + sentPacket = NULL; + } + return true; + } + // Timer event triggered - this is Timer T2 to resend Error. + else if (event->type == Timer) { + if (!parent->sendPacketZRTP(sentPacket)) { + sendFailed(); // returns to state Initial + return false; + } + if (nextTimer(&T2) <= 0) { + // returns to state initial + // timerFailed(ZrtpCodes.SevereCodes.SevereTooMuchRetries); + return false; + } + return true; + } + return false; +} + +int32_t ZrtpStateClass::startTimer(zrtpTimer_t *t) { + + t->time = t->start; + t->counter = 0; + return parent->activateTimer(t->time); +} + +int32_t ZrtpStateClass::nextTimer(zrtpTimer_t *t) { + + t->time += t->time; + t->time = (t->time > t->capping)? t->capping : t->time; + t->counter++; + if (t->counter > t->maxResend) { + return -1; + } + return parent->activateTimer(t->time); +} + +void ZrtpStateClass::sendErrorPacket(uint32_t errorCode) { + cancelTimer(); + + ZrtpPacketError* err = parent->prepareError(errorCode); + parent->zrtpNegotiationFailed(ZrtpError, errorCode); + + sentPacket = static_cast<ZrtpPacketBase *>(err); + nextState(WaitErrorAck); + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(err)) || (startTimer(&T2) <= 0)) { + sendFailed(); + } +} + +void ZrtpStateClass::sendSASRelay(ZrtpPacketSASrelay* relay) { + cancelTimer(); + sentPacket = static_cast<ZrtpPacketBase *>(relay); + secSubstate = WaitSasRelayAck; + if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(relay)) || (startTimer(&T2) <= 0)) { + sendFailed(); + } +} + +void ZrtpStateClass::sendFailed() { + sentPacket = NULL; + nextState(Initial); + parent->zrtpNegotiationFailed(Severe, SevereCannotSend); +} + +void ZrtpStateClass::timerFailed(int32_t subCode) { + sentPacket = NULL; + nextState(Initial); + parent->zrtpNegotiationFailed(Severe, subCode); +} + +void ZrtpStateClass::setMultiStream(bool multi) { + multiStream = multi; +} + +bool ZrtpStateClass::isMultiStream() { + return multiStream; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/ZrtpTextData.cpp b/src/ZrtpTextData.cpp new file mode 100644 index 0000000..5861099 --- /dev/null +++ b/src/ZrtpTextData.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2006-2008 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ +#include <stdint.h> +#include <libzrtpcpp/ZrtpConfigure.h> +// 1 +// 1234567890123456 +char clientId[] = "GNU ZRTP 2.1.0 "; // 16 chars max. +char zrtpVersion[] = "1.10"; // must be 4 chars +/** + * + */ +char HelloMsg[] = "Hello "; +char HelloAckMsg[] = "HelloACK"; +char CommitMsg[] = "Commit "; +char DHPart1Msg[] = "DHPart1 "; +char DHPart2Msg[] = "DHPart2 "; +char Confirm1Msg[] = "Confirm1"; +char Confirm2Msg[] = "Confirm2"; +char Conf2AckMsg[] = "Conf2ACK"; +char ErrorMsg[] = "Error "; +char ErrorAckMsg[] = "ErrorACK"; +char GoClearMsg[] = "GoClear "; +char ClearAckMsg[] = "ClearACK"; +char PingMsg[] = "Ping "; +char PingAckMsg[] = "PingACK "; +char SasRelayMsg[] = "SASrelay"; +char RelayAckMsg[] = "RelayACK"; + +char responder[] = "Responder"; +char initiator[] = "Initiator"; +char iniMasterKey[] = "Initiator SRTP master key"; +char iniMasterSalt[] = "Initiator SRTP master salt"; +char respMasterKey[] = "Responder SRTP master key"; +char respMasterSalt[] = "Responder SRTP master salt"; + +char iniHmacKey[] = "Initiator HMAC key"; +char respHmacKey[] = "Responder HMAC key"; +char retainedSec[] = "retained secret"; + +char iniZrtpKey[] = "Initiator ZRTP key"; +char respZrtpKey[] = "Responder ZRTP key"; + +char sasString[] = "SAS"; + +char KDFString[] = "ZRTP-HMAC-KDF"; + +char zrtpSessionKey[] = "ZRTP Session Key"; + +char zrtpMsk[] = "ZRTP MSK"; +char zrtpTrustedMitm[] = "Trusted MiTM key"; + +char s256[] = "S256"; +char s384[] = "S384"; +const char* mandatoryHash = s256; + +char aes3[] = "AES3"; +char aes2[] = "AES2"; +char aes1[] = "AES1"; +char two3[] = "2FS3"; +char two2[] = "2FS2"; +char two1[] = "2FS1"; +const char* mandatoryCipher = aes1; + +char dh2k[] = "DH2k"; +char ec25[] = "EC25"; +char dh3k[] = "DH3k"; +char ec38[] = "EC38"; +char mult[] = "Mult"; +const char* mandatoryPubKey = dh3k; + +char b32[] = "B32 "; +const char* mandatorySasType = b32; + +char hs32[] = "HS32"; +char hs80[] = "HS80"; +char sk32[] = "SK32"; +char sk64[] = "SK64"; +const char* mandatoryAuthLen_1 = hs32; +const char* mandatoryAuthLen_2 = hs80; diff --git a/src/libzrtpcpp/Base32.h b/src/libzrtpcpp/Base32.h new file mode 100644 index 0000000..fbe2983 --- /dev/null +++ b/src/libzrtpcpp/Base32.h @@ -0,0 +1,228 @@ +#ifndef BASE32_H +#define BASE32_H + +/* + * + * Copyright (c) 2002 Bryce "Zooko" Wilcox-O'Hearn Permission is hereby + * granted, free of charge, to any person obtaining a copy of this software to + * deal in this software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of this software, and to permit persons to whom this software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of this software. + * + * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THIS SOFTWARE. + * + * Converted to C++ by: + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +/** + * @file Base32.h + * @brief C++ implmentation of the Base32 encoding and decoding + * + * ZRTP uses the base 32 encoding and decoding to generate the Short + * Authentication String (SAS). + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <iostream> +#include <cstdlib> + +#include <string.h> +#include <assert.h> +#include <stddef.h> + +using namespace std; + +extern int divceil(int a, int b); + +class Base32 { + + public: + + /** + * A Constructor that decodes from base32 into binary. + * + * The constructor decodes the base32 encoded data back into binary + * data. Use <code>getDecoded(...)</code> to get the binary data. + * + * @param encoded + * The string that contains the base32 encoded data. + */ + Base32(const string encoded); + + /** + * A Constructor that decodes from base32 into binary. + * + * This constructor decodes the base32 encoded data back into + * binary data. Only the specified number of bits are decoded + * (should be a multiple of 5). Use + * <code>getDecoded(...)</code> to get the binary data. + * + * @param encoded + * The string that contains the base32 encoded data. + * @param noOfBits + * How many bits to decode into binary data. + */ + Base32(const string encoded, int noOfBits); + + /** + * A Constructor that encodes binary data. + * + * The constructor converts the first specified number of bits of + * the binary data into a base32 presentation. Use + * <code>getEncoded</code> to get the encoded data. + * + * @param data + * A pointer to the first bits (byte) of binary data + * @param noOfBits + * How many bits to use for encoding. Should be a + * multiple of 5. + */ + Base32(const unsigned char* data, int noOfBits); + + ~Base32(); + + /** + * Get the decoded binary data and its length. + * + * The method returns the decoded binary data if the appropriate + * Constructor was used. Otherwise we return <code>NULL</code> + * pointer and length zero. + * + * <em>Note:</em> This method returns a pointer to the decoded + * binary data. The Base32 object manages this pointer, thus you + * may need to copy the data to a save place before deleting this + * object. If the object is deleted this pointer is no longer + * valid. + * + * @param length + * A reference to an integer. + * @return + * A pointer to the decoded binary data. + */ + const unsigned char* getDecoded(int &length); + + /** + * Get the encoded base32 string. + * + * The method returns a string that contains the base32 encoded + * data if the appropriate constructor was used. Otherwise we + * return an empty string. + * + * @return + * The string containing the base32 encoded data. + */ + const string getEncoded() { return encoded; }; + + /** + * Compute the number of base32 encoded characters given the + * number of bits. + * + * @param lengthInBits + * The length of the data in bits + * @return + * The length of the base-32 encoding of the data in characters + */ + static size_t const b2alen(const size_t lengthInBits) { + return divceil(lengthInBits, 5); }; + + private: + + /** + * Decodes a string with base32 presentation into binary data. + * + * a2b_l() will return a result big enough to hold lengthinbits bits. So + * for example if cs is 4 characters long (encoding at least 15 and up to + * 20 bits) and lengthinbits is 16, then a2b_l() will return a string of + * length 2 (since 2 bytes is sufficient to store 16 bits). If cs is 4 + * characters long and lengthinbits is 20, then a2b_l() will return a + * string of length 3 (since 3 bytes is sufficient to store 20 bits). Note + * that `b2a_l()' does not mask off unused least-significant bits, so for + * example if cs is 4 characters long and lengthinbits is 17, then you + * must ensure that all three of the unused least-significant bits of cs + * are zero bits or you will get the wrong result. This precondition is + * tested by assertions if assertions are enabled. (Generally you just + * require the encoder to ensure this consistency property between the + * least significant zero bits and value of `lengthinbits', and reject + * strings that have a length-in-bits which isn't a multiple of 8 and yet + * don't have trailing zero bits, as improperly encoded.) + * + * @param cs + * The data to be decoded + * @param size + * The length of the input data buffer. Usually divceil(length in bits, 5). + * @param lengthinbits + * The number of bits of data in <code>cs</code> to be decoded + */ + void a2b_l(const string cs, size_t size, const size_t lengthinbits); + + /** + * Encodes binary to to base32 presentation. + * + * b2a_l() will generate a base-32 encoded string big enough to encode + * lengthinbits bits. So for example if os is 2 bytes long and + * lengthinbits is 15, then b2a_l() will generate a 3-character- long + * base-32 encoded string (since 3 quintets is sufficient to encode 15 + * bits). If os is 2 bytes long and lengthinbits is 16 (or None), then + * b2a_l() will generate a 4-character string. Note that `b2a_l()' does + * not mask off unused least-significant bits, so for example if os is 2 + * bytes long and lengthinbits is 15, then you must ensure that the unused + * least-significant bit of os is a zero bit or you will get the wrong + * result. This precondition is tested by assertions if assertions are + * enabled. + * + * Warning: if you generate a base-32 encoded string with `b2a_l()', and + * then someone else tries to decode it by calling `a2b()' instead of + * `a2b_l()', then they will (probably) get a different string than the + * one you encoded! So only use `b2a_l()' when you are sure that the + * encoding and decoding sides know exactly which `lengthinbits' to use. + * If you do not have a way for the encoder and the decoder to agree upon + * the lengthinbits, then it is best to use `b2a()' and `a2b()'. The only + * drawback to using `b2a()' over `b2a_l()' is that when you have a number + * of bits to encode that is not a multiple of 8, `b2a()' can sometimes + * generate a base-32 encoded string that is one or two characters longer + * than necessary. + * + * @param cs + * Pointer to binary data. + * @param len + * Length of the binary data buffer. Usually (noOfBits+7)/8. + * @param noOfBits + * The number of bits of data in encoded into `cs' + */ + void b2a_l(const unsigned char* cs, int len, const size_t noOfBits); + + /** + * Holds the pointer to decoded binary data + */ + unsigned char *binaryResult; + + /** + * Length of decoding result + */ + int resultLength; + + /** + * The string containing the base32 encoded data. + */ + string encoded; + + unsigned char smallBuffer[128]; +}; + +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/CMakeLists.txt b/src/libzrtpcpp/CMakeLists.txt new file mode 100755 index 0000000..099a233 --- /dev/null +++ b/src/libzrtpcpp/CMakeLists.txt @@ -0,0 +1,9 @@ + +if(enable_ccrtp) + set(ccrtp_inst ZrtpQueue.h zrtpccrtp.h ZrtpUserCallback.h TimeoutProvider.h) +endif() + +install(FILES + ZrtpCodes.h ZrtpConfigure.h ZrtpCallback.h ZrtpCWrapper.h + ${ccrtp_inst} DESTINATION include/libzrtpcpp) + diff --git a/src/libzrtpcpp/TimeoutProvider.h b/src/libzrtpcpp/TimeoutProvider.h new file mode 100644 index 0000000..9f0edf7 --- /dev/null +++ b/src/libzrtpcpp/TimeoutProvider.h @@ -0,0 +1,303 @@ +/* + Copyright (C) 2006, 2005, 2004 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#ifndef TIMEOUTPROVIDER_H +#define TIMEOUTPROVIDER_H + +/** + * Provides a way to request timeouts after a number of milli seconds. + * + * A command is associated to each timeout. + * + * Modified to use the common c++ library functions and the STL + * list by Werner Dittmann. + * + * @author Erik Eliasson, eliasson@it.kth.se, 2003 + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <list> +#include <sys/time.h> + +#include <commoncpp/config.h> +#include <commoncpp/thread.h> + +/** + * Represents a request of a "timeout" (delivery of a command to a + * "timeout receiver" after at least a specified time period). + * + * Slightly modified to use gettimeofday directly. + * + * NOTE: This class is only used internaly. + * @author Erik Eliasson + * @author Werner Dittmann + */ +template <class TOCommand, class TOSubscriber> +class TPRequest +{ + +public: + + TPRequest( TOSubscriber tsi, int timeoutMs, const TOCommand &command): + subscriber(tsi) + { + struct timeval tv; + gettimeofday(&tv, NULL ); + + when_ms = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000; + when_ms += timeoutMs; + this->command = command; + } + + /** + * @param t ms since Epoch + */ + bool happensBefore(uint64 t) + { + if (when_ms < t) { + return true; + } + if (when_ms > t) { + return false; + } + return false; // if equal it does not "happens_before" + + } + + bool happensBefore(const TPRequest *req){ + return happensBefore(req->when_ms); + } + + /** + * Number of milli seconds until timeout from when this method is + * called + */ + int getMsToTimeout () + { + struct timeval tv; + gettimeofday(&tv, NULL ); + + uint64 now = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000; + + if (happensBefore(now)) { + return 0; + } + else { + return (int)(when_ms - now); + } + } + + TOCommand getCommand() + { + return command; + } + + TOSubscriber getSubscriber() + { + return subscriber; + } + + /** + * Two timeout requests are considered equeal if they have + * the same subscriber AND command AND time when they + * occur. If one of the time is zero then this is a + * wildcard and matches always. + */ + bool operator==(const TPRequest<TOCommand, TOSubscriber> &req) + { + if (req.subscriber == subscriber && + req.command == command && + req.when_ms == when_ms) { + return true; + } + return false; + } + +private: + TOSubscriber subscriber; + uint64 when_ms; // Time since Epoch in ms when the timeout + // will happen + + TOCommand command; // Command that will be delivered to the + // receiver (subscriber) of the timeout. +}; + +/** + * Class to generate objects giving timeout functionality. + * + * @author Erik Eliasson + * @author Werner Dittmann + */ +template<class TOCommand, class TOSubscriber> + class TimeoutProvider : public ost::Thread, ost::Event { + +public: + + /** + * Timeout Provide Constructor + */ + TimeoutProvider(): requests(), synchLock(), stop(false) { } + + /** + * Destructor also terminates the Timeout thread. + */ + ~TimeoutProvider() { + terminate(); + } + + /** + * Terminates the Timeout provider thread. + */ + void stopThread(){ + stop = true; + signal(); // signal event to waiting thread + } + + /** + * Request a timeout trigger. + * + * @param time_ms Number of milli-seconds until the timeout is + * wanted. Note that a small additional period of time is + * added that depends on execution speed. + * @param subscriber The receiver of the callback when the command has timed + * out. This argument must not be NULL. + * @param command Specifies the String command to be passed back in the + * callback. + */ + void requestTimeout(int32_t time_ms, TOSubscriber subscriber, const TOCommand &command) + { + TPRequest<TOCommand, TOSubscriber>* request = + new TPRequest<TOCommand, TOSubscriber>(subscriber, time_ms, command); + + synchLock.enter(); + + if (requests.size()==0) { + requests.push_front(request); + signal(); + synchLock.leave(); + return; + } + if (request->happensBefore(requests.front())) { + requests.push_front(request); + signal(); + synchLock.leave(); + return; + } + if (requests.back()->happensBefore(request)){ + requests.push_back(request); + signal(); + synchLock.leave(); + return; + } + + typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i; + for(i = requests.begin(); i != requests.end(); i++ ) { + if( request->happensBefore(*i)) { + requests.insert(i, request); + break; + } + } + signal(); + synchLock.leave(); + } + + /** + * Removes timeout requests that belong to a subscriber and command. + * + * @see requestTimeout + */ + void cancelRequest(TOSubscriber subscriber, const TOCommand &command) + { + synchLock.enter(); + typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i; + for(i = requests.begin(); i != requests.end(); ) { + if( (*i)->getCommand() == command && + (*i)->getSubscriber() == subscriber) { + i = requests.erase(i); + continue; + } + i++; + } + synchLock.leave(); + } + +protected: + + void run() + { + do { + synchLock.enter(); + int32_t time = 3600000; + int32_t size = 0; + if ((size = requests.size()) > 0) { + time = requests.front()->getMsToTimeout(); + } + if (time == 0 && size > 0) { + if (stop){ // This must be checked so that we will + // stop even if we have timeouts to deliver. + synchLock.leave(); + return; + } + TPRequest<TOCommand, TOSubscriber>* req = requests.front(); + TOSubscriber subs = req->getSubscriber(); + TOCommand command = req->getCommand(); + + requests.pop_front(); + + synchLock.leave(); // call the command with free Mutex + subs->handleTimeout(command); + continue; + } + synchLock.leave(); + if (stop) { // If we were told to stop while delivering + // a timeout we will exit here + return; + } + reset(); // ready to receive triggers again + wait(time); + if (stop) { // If we are told to exit while waiting we + // will exit + return; + } + } while(true); + } + +private: + + // The timeouts are ordered in the order of which they + // will expire. Nearest in future is first in list. + std::list<TPRequest<TOCommand, TOSubscriber> *> requests; + + ost::Mutex synchLock; // Protects the internal data structures + + bool stop; // Flag to tell the worker thread + // to terminate. Set to true and + // wake the worker thread to + // terminate it. +}; + +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/ZIDFile.h b/src/libzrtpcpp/ZIDFile.h new file mode 100644 index 0000000..0637c27 --- /dev/null +++ b/src/libzrtpcpp/ZIDFile.h @@ -0,0 +1,157 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> + +#include <libzrtpcpp/ZIDRecord.h> + +#ifndef _ZIDFILE_H_ +#define _ZIDFILE_H_ +/** + * @file ZIDFile.h + * @brief ZID file management + * + * A ZID file stores (caches) some data that helps ZRTP to achives its + * key continuity feature. See @c ZIDRecord for further info which data + * the ZID file contains. + * + * @ingroup GNU_ZRTP + * @{ + */ + +/** + * This class implements a ZID (ZRTP Identifiers) file. + * + * The ZID file holds information about peers. + * + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZIDFile { + +private: + + FILE* zidFile; + unsigned char associatedZid[IDENTIFIER_LEN]; + /** + * The private ZID file constructor. + * + */ + ZIDFile(): zidFile(NULL) {}; + ~ZIDFile(); + void createZIDFile(char* name); + void checkDoMigration(char* name); + +public: + + /** + * Get the an instance of ZIDFile. + * + * This method just creates an instance an store a pointer to it + * in a static variable. The ZIDFile is a singleton, thus only + * <em>one</em> ZID file can be open at one time. + * + * @return + * A pointer to the global ZIDFile singleton instance. + */ + static ZIDFile* getInstance(); + /** + * Open the named ZID file and return a ZID file class. + * + * This static function either opens an existing ZID file or + * creates a new ZID file with the given name. The ZIDFile is a + * singleton, thus only <em>one</em> ZID file can be open at one + * time. + * + * To open another ZID file you must close the active ZID file + * first. + * + * @param name + * The name of the ZID file to open or create + * @return + * 1 if file could be opened/created, 0 if the ZID instance + * already has an open file, -1 if open/creation of file failed. + */ + int open(char *name); + + /** + * Check if ZIDFile has an active (open) file. + * + * @return + * True if ZIDFile has an active file, false otherwise + */ + bool isOpen() { return (zidFile != NULL); }; + + /** + * Close the ZID file. + * Closes the ZID file, and prepares to open a new ZID file. + */ + void close(); + + /** + * Get a ZID record from the active ZID file. + * + * The method get the identifier data from the ZID record parameter, + * locates the record in the ZID file and fills in the RS1, RS2, and + * other data. + * + * If no matching record exists in the ZID file the method creates + * it and fills it with default values. + * + * @param zidRecord + * The ZID record that contains the identifier data. The method + * fills in data . + * @return + * Currently always 1 to indicate sucess + */ + unsigned int getRecord(ZIDRecord* zidRecord); + + /** + * Save a ZID record into the active ZID file. + * + * This method saves the content of a ZID record into the ZID file. Before + * you can save the ZID record you must have performed a getRecord() + * first. + * + * @param zidRecord + * The ZID record to save. + * @return + * 1 on success + */ + unsigned int saveRecord(ZIDRecord *zidRecord); + + /** + * Get the ZID associated with this ZID file. + * + * @return + * Pointer to the ZID + */ + const unsigned char* getZid() { return associatedZid; }; +}; + +/** + * @} + */ +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/ZIDRecord.h b/src/libzrtpcpp/ZIDRecord.h new file mode 100644 index 0000000..eb2d190 --- /dev/null +++ b/src/libzrtpcpp/ZIDRecord.h @@ -0,0 +1,307 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZIDRECORD_H_ +#define _ZIDRECORD_H_ + + +/** + * @file ZIDRecord.h + * @brief ZID record management + * + * A ZID record stores (caches) ZID (ZRTP ID) specific data that helps ZRTP + * to achives its key continuity feature. Please refer to the ZRTP + * specification to get detailed information about the ZID. + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <string.h> +#include <stdint.h> + +#define IDENTIFIER_LEN 12 +#define RS_LENGTH 32 +#define TIME_LENGTH 8 // 64 bit, can hold time on 64 bit systems + +/** + * This is the recod structure of version 1 ZID records. + * + * This is not longer in use - only during migration. + */ +typedef struct zidrecord1 { + char recValid; //!< if 1 record is valid, if 0: invalid + char ownZid; //!< if >1 record contains own ZID, usually 1st record + char rs1Valid; //!< if 1 RS1 contains valid data + char rs2Valid; //!< if 1 RS2 contains valid data + unsigned char identifier[IDENTIFIER_LEN]; ///< the peer's ZID or own ZID + unsigned char rs1Data[RS_LENGTH], rs2Data[RS_LENGTH]; ///< the peer's RS data +} zidrecord1_t; + +/** + * This is the recod structure of version 2 ZID records. + */ +typedef struct zidrecord2 { + char version; ///< version number of file format, this is #2 + char flags; ///< bit field holding various flags, see below + char filler1; ///< round up to next 32 bit + char filler2; ///< round up to next 32 bit + unsigned char identifier[IDENTIFIER_LEN]; ///< the peer's ZID or own ZID + unsigned char rs1Interval[TIME_LENGTH]; ///< expiration time of RS1; -1 means indefinite + unsigned char rs1Data[RS_LENGTH]; ///< the peer's RS2 data + unsigned char rs2Interval[TIME_LENGTH]; ///< expiration time of RS2; -1 means indefinite + unsigned char rs2Data[RS_LENGTH]; ///< the peer's RS2 data + unsigned char mitmKey[RS_LENGTH]; ///< MiTM key if available +} zidrecord2_t; + + +#ifndef __EXPORT + #if __GNUC__ >= 4 + #define __EXPORT __attribute__ ((visibility("default"))) + #define __LOCAL __attribute__ ((visibility("hidden"))) + #elif defined _WIN32 || defined __CYGWIN__ + #define __EXPORT __declspec(dllimport) + #define __LOCAL + #else + #define __EXPORT + #define __LOCAL + #endif +#endif + +static const int Valid = 0x1; +static const int SASVerified = 0x2; +static const int RS1Valid = 0x4; +static const int RS2Valid = 0x8; +static const int MITMKeyAvailable = 0x10; +static const int OwnZIDRecord = 0x20; + +/** + * This class implements the ZID record. + * + * The ZID record holds data about a peer. According to ZRTP specification + * we use a ZID to identify a peer. ZRTP uses the RS (Retained Secret) data + * to construct shared secrets. + * <p> + * NOTE: ZIDRecord has ZIDFile as friend. ZIDFile knows about the private + * data of ZIDRecord - please keep both classes synchronized. + * + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZIDRecord { + friend class ZIDFile; + +private: + zidrecord2_t record; + unsigned long position; + + /* + * The default constructor is private + */ + ZIDRecord() { + record.version = 2; + } + + /** + * Functions for I/O availabe for ZID file handling + * + * These functions are private, thus only friends may use it. + */ + void setPosition(long pos) {position = pos;} + long getPosition() {return position; } + + zidrecord2_t* getRecordData() {return &record; } + int getRecordLength() {return sizeof(zidrecord2_t); } + + bool isValid() { return ((record.flags & Valid) == Valid); } + void setValid() { record.flags |= Valid; } + +public: + /** + * Create a ZID Record with given ZID data + * + * The method creates a new ZID record and initializes its ZID + * data field. All other fields are set to null. + * + * An application can use this pre-initialized record to look + * up the associated record in the ZID file. If the record is + * available, the ZID record fields are filled with the stored + * data. + * + * @param idData + * Pointer to the fixed length ZID data + * @see ZIDFile::getRecord + */ + ZIDRecord(const unsigned char *idData) { + memset(&record, 0, sizeof(zidrecord2_t)); + memcpy(record.identifier, idData, IDENTIFIER_LEN); + record.version = 2; + } + + /** + * Set @c valid flag in RS1 + */ + void setRs1Valid() { record.flags |= RS1Valid; } + + /** + * reset @c valid flag in RS1 + */ + void resetRs1Valid() { record.flags &= ~RS1Valid; } + + /** + * Check @c valid flag in RS1 + */ + bool isRs1Valid() { return ((record.flags & RS1Valid) == RS1Valid); } + + /** + * Set @c valid flag in RS2 + */ + void setRs2Valid() { record.flags |= RS2Valid; } + + /** + * Reset @c valid flag in RS2 + */ + void resetRs2Valid() { record.flags &= ~RS2Valid; } + + /** + * Check @c valid flag in RS2 + */ + bool isRs2Valid() { return ((record.flags & RS2Valid) == RS2Valid); } + + /** + * Set MITM key available + */ + void setMITMKeyAvailable() { record.flags |= MITMKeyAvailable; } + + /** + * Reset MITM key available + */ + void resetMITMKeyAvailable() { record.flags &= ~MITMKeyAvailable; } + + /** + * Check MITM key available is set + */ + bool isMITMKeyAvailable() { return ((record.flags & MITMKeyAvailable) == MITMKeyAvailable); } + + /** + * Mark this as own ZID record + */ + void setOwnZIDRecord() { record.flags = OwnZIDRecord; } + /** + * Reset own ZID record marker + */ + void resetOwnZIDRecord(){ record.flags = 0; } + + /** + * Check own ZID record marker + */ + bool isOwnZIDRecord() { return (record.flags == OwnZIDRecord); } // no other flag allowed if own ZID + + /** + * Set SAS for this ZID as verified + */ + void setSasVerified() { record.flags |= SASVerified; } + /** + * Reset SAS for this ZID as verified + */ + void resetSasVerified() { record.flags &= ~SASVerified; } + + /** + * Check if SAS for this ZID was verified + */ + bool isSasVerified() { return ((record.flags & SASVerified) == SASVerified); } + + /** + * Return the ZID for this record + */ + const uint8_t* getIdentifier() {return record.identifier; } + + /** + * Check if RS1 is still valid + * + * Returns true if RS1 is still valid, false otherwise. + * + * @return + * Returns true is RS1 is not expired (valid), false otherwise. + */ + const bool isRs1NotExpired(); + + /** + * Returns pointer to RS1 data. + */ + const unsigned char* getRs1() { return record.rs1Data; } + + /** + * Check if RS2 is still valid + * + * Returns true if RS2 is still valid, false otherwise. + * + * @return + * Returns true is RS2 is not expired (valid), false otherwise. + */ + const bool isRs2NotExpired(); + + /** + * Returns pointer to RS1 data. + */ + const unsigned char* getRs2() { return record.rs2Data; } + + /** + * Sets new RS1 data and associated expiration value. + * + * If the expiration value is >0 or -1 the method stores the new + * RS1. Before it stores the new RS1 it shifts the exiting RS1 + * into RS2 (together with its expiration time). Then it computes + * the expiration time of the and stores the result together with + * the new RS1. + * + * If the expiration value is -1 then this RS will never expire. + * + * If the expiration value is 0 then the expiration value of a + * stored RS1 is cleared and no new RS1 value is stored. Also RS2 + * is left unchanged. + * + * @param data + * Points to the new RS1 data. + * @param expire + * The expiration interval in seconds. Default is -1. + * + */ + void setNewRs1(const unsigned char* data, int32_t expire =-1); + + /** + * Set MiTM key data. + * + */ + void setMiTMData(const unsigned char* data); + + /** + * Get MiTM key data. + * + */ + const unsigned char* getMiTMData() {return record.mitmKey; } +}; + +#endif // ZIDRECORD + + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/ZRtp.h b/src/libzrtpcpp/ZRtp.h new file mode 100644 index 0000000..4e03f28 --- /dev/null +++ b/src/libzrtpcpp/ZRtp.h @@ -0,0 +1,1292 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTP_H_ +#define _ZRTP_H_ +/** + * @file ZRtp.h + * @brief The ZRTP main engine + * @defgroup GNU_ZRTP The GNU ZRTP C++ implementation + * @{ + */ + +#include <cstdlib> + +#include <libzrtpcpp/ZrtpPacketHello.h> +#include <libzrtpcpp/ZrtpPacketHelloAck.h> +#include <libzrtpcpp/ZrtpPacketCommit.h> +#include <libzrtpcpp/ZrtpPacketDHPart.h> +#include <libzrtpcpp/ZrtpPacketConfirm.h> +#include <libzrtpcpp/ZrtpPacketConf2Ack.h> +#include <libzrtpcpp/ZrtpPacketGoClear.h> +#include <libzrtpcpp/ZrtpPacketClearAck.h> +#include <libzrtpcpp/ZrtpPacketError.h> +#include <libzrtpcpp/ZrtpPacketErrorAck.h> +#include <libzrtpcpp/ZrtpPacketPing.h> +#include <libzrtpcpp/ZrtpPacketPingAck.h> +#include <libzrtpcpp/ZrtpPacketSASrelay.h> +#include <libzrtpcpp/ZrtpPacketRelayAck.h> +#include <libzrtpcpp/ZrtpCallback.h> +#include <libzrtpcpp/ZIDRecord.h> + +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + +// Prepare to support digest algorithms up to 512 bit (64 bytes) +#define MAX_DIGEST_LENGTH 64 +#define IMPL_MAX_DIGEST_LENGTH 64 + +class __EXPORT ZrtpStateClass; +class ZrtpDH; + +/** + * The main ZRTP class. + * + * This is the main class of the RTP/SRTP independent part of the GNU + * ZRTP. It handles the ZRTP HMAC, DH, and other data management. The + * user of this class needs to know only a few methods and needs to + * provide only a few external functions to connect to a Timer + * mechanism and to send data via RTP and SRTP. Refer to the + * ZrtpCallback class to get detailed information regading the + * callback methods required by GNU RTP. + * + * The class ZrtpQueue is the GNU ccRTP specific implementation that + * extends standard ccRTP RTP provide ZRTP support. Refer to the + * documentation of ZrtpQueue to get more information about the usage + * of ZRtp and associated classes. + * + * The main entry into the ZRTP class is the processExtensionHeader() + * method. + * + * This class does not directly handle the protocol states, timers, + * and packet resend. The protocol state engine is responsible for + * these actions. + * + * Example how to use ZRtp: + *<pre> + * zrtpEngine = new ZRtp((uint8_t*)ownZid, (ZrtpCallback*)this, idString); + * zrtpEngine->startZrtpEngine(); + *</pre> + * @see ZrtpCallback + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZRtp { + + public: + + /** + * Constructor intializes all relevant data but does not start the + * engine. + */ + ZRtp(uint8_t* myZid, ZrtpCallback* cb, std::string id, + ZrtpConfigure* config, bool mitmm= false, bool sasSignSupport= false); + + /** + * Destructor cleans up. + */ + ~ZRtp(); + + /** + * Kick off the ZRTP protocol engine. + * + * This method calls the ZrtpStateClass#evInitial() state of the state + * engine. After this call we are able to process ZRTP packets + * from our peer and to process them. + */ + void startZrtpEngine(); + + /** + * Stop ZRTP security. + * + */ + void stopZrtp(); + + /** + * Process RTP extension header. + * + * This method expects to get a pointer to the extension header of + * a RTP packet. The method checks if this is really a ZRTP + * packet. If this check fails the method returns 0 (false) in + * case this is not a ZRTP packet. We return a 1 if we processed + * the ZRTP extension header and the caller may process RTP data + * after the extension header as usual. The method return -1 the + * call shall dismiss the packet and shall not forward it to + * further RTP processing. + * + * @param extHeader + * A pointer to the first byte of the extension header. Refer to + * RFC3550. + * @param peerSSRC + * The peer's SSRC. + * @return + * Code indicating further packet handling, see description above. + */ + void processZrtpMessage(uint8_t *extHeader, uint32_t peerSSRC); + + /** + * Process a timeout event. + * + * We got a timeout from the timeout provider. Forward it to the + * protocol state engine. + * + */ + void processTimeout(); + + /** + * Check for and handle GoClear ZRTP packet header. + * + * This method checks if this is a GoClear packet. If not, just return + * false. Otherwise handle it according to the specification. + * + * @param extHeader + * A pointer to the first byte of the extension header. Refer to + * RFC3550. + * @return + * False if not a GoClear, true otherwise. + */ + bool handleGoClear(uint8_t *extHeader); + + /** + * Set the auxilliary secret. + * + * Use this method to set the auxilliary secret data. Refer to ZRTP + * specification, chapter 4.3 ff + * + * @param data + * Points to the secret data. + * @param length + * Length of the auxilliary secrect in bytes + */ + void setAuxSecret(uint8_t* data, int32_t length); + + /** + * Check current state of the ZRTP state engine + * + * @param state + * The state to check. + * @return + * Returns true id ZRTP engine is in the given state, false otherwise. + */ + bool inState(int32_t state); + + /** + * Set SAS as verified. + * + * Call this method if the user confirmed (verfied) the SAS. ZRTP + * remembers this together with the retained secrets data. + */ + void SASVerified(); + + /** + * Reset the SAS verfied flag for the current active user's retained secrets. + * + */ + void resetSASVerified(); + + /** + * Get the ZRTP Hello Hash data. + * + * Use this method to get the ZRTP Hello Hash data. The method + * returns the data as a string containing the ZRTP protocol version and + * hex-digits. + * + * Refer to ZRTP specification, chapter 8. + * + * @return + * a std:string containing the Hello hash value as hex-digits. The + * hello hash is available immediately after class instantiation. + */ + std::string getHelloHash(); + + /** + * Get the peer's ZRTP Hello Hash data. + * + * Use this method to get the peer's ZRTP Hello Hash data. The method + * returns the data as a string containing the ZRTP protocol version and + * hex-digits. + * + * The peer's hello hash is available only after ZRTP received a hello. If + * no data is available the function returns an empty string. + * + * Refer to ZRTP specification, chapter 8. + * + * @return + * a std:string containing the Hello version and the hello hash as hex digits. + */ + std::string getPeerHelloHash(); + + /** + * Get Multi-stream parameters. + * + * Use this method to get the Multi-stream that were computed during + * the ZRTP handshake. An application may use these parameters to + * enable multi-stream processing for an associated SRTP session. + * + * Refer to chapter 4.4.2 in the ZRTP specification for further details + * and restriction how and when to use multi-stream mode. + * + * @return + * a string that contains the multi-stream parameters. The application + * must not modify the contents of this string, it is opaque data. The + * application may hand over this string to a new ZrtpQueue instance + * to enable multi-stream processing for this ZrtpQueue. + * If ZRTP was not started or ZRTP is not yet in secure state the method + * returns an empty string. + */ + std::string getMultiStrParams(); + + /** + * Set Multi-stream parameters. + * + * Use this method to set the parameters required to enable Multi-stream + * processing of ZRTP. The multi-stream parameters must be set before the + * application starts the ZRTP protocol engine. + * + * Refer to chapter 4.4.2 in the ZRTP specification for further details + * of multi-stream mode. + * + * @param parameters + * A string that contains the multi-stream parameters that this + * new ZrtpQueue instanace shall use. See also + * <code>getMultiStrParams()</code> + */ + void setMultiStrParams(std::string parameters); + + /** + * Check if this ZRTP session is a Multi-stream session. + * + * Use this method to check if this ZRTP instance uses multi-stream. + * Refer to chapters 4.2 and 4.4.2 in the ZRTP. + * + * @return + * True if multi-stream is used, false otherwise. + */ + bool isMultiStream(); + + /** + * Check if the other ZRTP client supports Multi-stream. + * + * Use this method to check if the other ZRTP client supports + * Multi-stream mode. + * + * @return + * True if multi-stream is available, false otherwise. + */ + bool isMultiStreamAvailable(); + + /** + * Accept a PBX enrollment request. + * + * If a PBX service asks to enroll the PBX trusted MitM key and the user + * accepts this request, for example by pressing an OK button, the client + * application shall call this method and set the parameter + * <code>accepted</code> to true. If the user does not accept the request + * set the parameter to false. + * + * @param accepted + * True if the enrollment request is accepted, false otherwise. + */ + void acceptEnrollment(bool accepted); + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and perform the enrollment actions. A MitM (PBX) enrollment service + * started this ZRTP session. Can be set to true only if mitmMode is also true. + * + * @return status of the enrollmentMode flag. + */ + bool isEnrollmentMode(); + + /** + * Set the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and perform the enrollment actions. A MitM (PBX) enrollment + * service must sets this mode to true. + * + * Can be set to true only if mitmMode is also true. + * + * @param enrollmentMode defines the new state of the enrollmentMode flag + */ + void setEnrollmentMode(bool enrollmentMode); + + /** + * Check if a peer's cache entry has a vaild MitM key. + * + * If true then the other peer ha a valid MtiM key, i.e. the peer has performed + * the enrollment procedure. A PBX ZRTP Back-2-Back application can use this function + * to check which of the peers is enrolled. + * + * @return True if the other peer has a valid Mitm key (is enrolled). + */ + bool isPeerEnrolled(); + + /** + * Send the SAS relay packet. + * + * The method creates and sends a SAS relay packet according to the ZRTP + * specifications. Usually only a MitM capable user agent (PBX) uses this + * function. + * + * @param sh the full SAS hash value, 32 bytes + * @param render the SAS rendering algorithm + */ + bool sendSASRelayPacket(uint8_t* sh, std::string render); + + /** + * Get the commited SAS rendering algorithm for this ZRTP session. + * + * @return the commited SAS rendering algorithm + */ + std::string getSasType(); + + /** + * Get the computed SAS hash for this ZRTP session. + * + * A PBX ZRTP back-to-Back function uses this function to get the SAS + * hash of an enrolled client to construct the SAS relay packet for + * the other client. + * + * @return a pointer to the byte array that contains the full + * SAS hash. + */ + uint8_t* getSasHash(); + + /** + * Set signature data. + * + * This functions stores signature data and transmitts it during ZRTP + * processing to the other party as part of the Confirm packets. Refer to + * chapters 5.7 and 7.2. + * + * The signature data must be set before ZRTP the application calls + * <code>start()</code>. + * + * @param data + * The signature data including the signature type block. The method + * copies this data into the Confirm packet at signature type block. + * @param length + * The length of the signature data in bytes. This length must be + * multiple of 4. + * @return + * True if the method stored the data, false otherwise. + */ + bool setSignatureData(uint8_t* data, int32_t length); + + /** + * Get signature data. + * + * This functions returns a pointer to the signature data that was receivied + * during ZRTP processing. Refer to chapters 5.7 and 7.2. + * + * The returned pointer points to volatile data that is valid only during the + * <code>checkSASSignature()</code> callback funtion. The application must copy + * the signature data if it will be used after the callback function returns. + * + * The signature data can be retrieved after ZRTP enters secure state. + * <code>start()</code>. + * + * @return + * Pointer to signature data. + */ + const uint8_t* getSignatureData(); + + /** + * Get length of signature data in number of bytes. + * + * This functions returns the length of signature data that was receivied + * during ZRTP processing. Refer to chapters 5.7 and 7.2. + * + * @return + * Length in bytes of the received signature data. The method returns + * zero if no signature data is avilable. + */ + int32_t getSignatureLength(); + + /** + * Emulate a Conf2Ack packet. + * + * This method emulates a Conf2Ack packet. According to ZRTP specification + * the first valid SRTP packet that the Initiator receives must switch + * on secure mode. Refer to chapter 4 in the specificaton + * + */ + void conf2AckSecure(); + + /** + * Get other party's ZID (ZRTP Identifier) data + * + * This functions returns the other party's ZID that was receivied + * during ZRTP processing. + * + * The ZID data can be retrieved after ZRTP receive the first Hello + * packet from the other party. The application may call this method + * for example during SAS processing in showSAS(...) user callback + * method. + * + * @param data + * Pointer to a data buffer. This buffer must have a size of + * at least 12 bytes (96 bit) (ZRTP Identifier, see chap. 4.9) + * @return + * Number of bytes copied into the data buffer - must be equivalent + * to 96 bit, usually 12 bytes. + */ + int32_t getPeerZid(uint8_t* data); + +private: + friend class ZrtpStateClass; + + /** + * The state engine takes care of protocol processing. + */ + ZrtpStateClass* stateEngine; + + /** + * This is my ZID that I send to the peer. + */ + uint8_t zid[IDENTIFIER_LEN]; + + /** + * The peer's ZID + */ + uint8_t peerZid[IDENTIFIER_LEN]; + + /** + * The callback class provides me with the interface to send + * data and to deal with timer management of the hosting system. + */ + ZrtpCallback* callback; + + /** + * My active Diffie-Helman context + */ + ZrtpDH* dhContext; + + /** + * The computed DH shared secret + */ + uint8_t* DHss; + + /** + * My computed public key + */ + uint8_t pubKeyBytes[400]; + /** + * Length off public key + */ +// int32_t pubKeyLen; + /** + * My Role in the game + */ + Role myRole; + + /** + * The human readable SAS value + */ + std::string SAS; + + /** + * The SAS hash for signaling and alike. Refer to chapters + * 4.5 and 7 how sasHash, sasValue and the SAS string are derived. + */ + uint8_t sasHash[MAX_DIGEST_LENGTH]; + /** + * The ids for the retained and other shared secrets + */ + uint8_t rs1IDr[MAX_DIGEST_LENGTH]; + uint8_t rs2IDr[MAX_DIGEST_LENGTH]; + uint8_t auxSecretIDr[MAX_DIGEST_LENGTH]; + uint8_t pbxSecretIDr[MAX_DIGEST_LENGTH]; + + uint8_t rs1IDi[MAX_DIGEST_LENGTH]; + uint8_t rs2IDi[MAX_DIGEST_LENGTH]; + uint8_t auxSecretIDi[MAX_DIGEST_LENGTH]; + uint8_t pbxSecretIDi[MAX_DIGEST_LENGTH]; + + /** + * pointers to aux secret storage and length of aux secret + */ + uint8_t* auxSecret; + int32_t auxSecretLength; + + /** + * Record if valid rs1 and/or rs1 were found in the + * retaind secret cache. + */ + bool rs1Valid; + bool rs2Valid; + /** + * My hvi + */ + uint8_t hvi[MAX_DIGEST_LENGTH]; + + /** + * The peer's hvi + */ + uint8_t peerHvi[8*ZRTP_WORD_SIZE]; + + /** + * Context to compute the SHA256 hash of selected messages. + * Used to compute the s0, refer to chapter 4.4.1.4 + */ + void* msgShaContext; + /** + * Commited Hash, Cipher, and public key algorithms + */ + AlgorithmEnum* hash; + AlgorithmEnum* cipher; + AlgorithmEnum* pubKey; + /** + * The selected SAS type. + */ + AlgorithmEnum* sasType; + + /** + * The selected SAS type. + */ + AlgorithmEnum* authLength; + + /** + * The Hash images as defined in chapter 5.1.1 (H0 is a random value, + * not stored here). Need full SHA 256 lenght to store hash value but + * only the leftmost 128 bits are used in computations and comparisons. + */ + uint8_t H0[IMPL_MAX_DIGEST_LENGTH]; + uint8_t H1[IMPL_MAX_DIGEST_LENGTH]; + uint8_t H2[IMPL_MAX_DIGEST_LENGTH]; + uint8_t H3[IMPL_MAX_DIGEST_LENGTH]; + uint8_t helloHash[IMPL_MAX_DIGEST_LENGTH]; + + uint8_t peerHelloHash[IMPL_MAX_DIGEST_LENGTH]; + uint8_t peerHelloVersion[ZRTP_WORD_SIZE + 1]; // +1 for nul byte + + // We get the peer's H? from the message where length is defined as 8 words + uint8_t peerH0[8*ZRTP_WORD_SIZE]; + uint8_t peerH1[8*ZRTP_WORD_SIZE]; + uint8_t peerH2[8*ZRTP_WORD_SIZE]; + uint8_t peerH3[8*ZRTP_WORD_SIZE]; + + /** + * The SHA256 hash over selected messages + */ + uint8_t messageHash[MAX_DIGEST_LENGTH]; + + /** + * The s0 + */ + uint8_t s0[MAX_DIGEST_LENGTH]; + + /** + * The new Retained Secret + */ + uint8_t newRs1[MAX_DIGEST_LENGTH]; + + /** + * The GoClear HMAC keys and confirm HMAC key + */ + uint8_t hmacKeyI[MAX_DIGEST_LENGTH]; + uint8_t hmacKeyR[MAX_DIGEST_LENGTH]; + + /** + * The Initiator's srtp key and salt + */ + uint8_t srtpKeyI[MAX_DIGEST_LENGTH]; + uint8_t srtpSaltI[MAX_DIGEST_LENGTH]; + + /** + * The Responder's srtp key and salt + */ + uint8_t srtpKeyR[MAX_DIGEST_LENGTH]; + uint8_t srtpSaltR[MAX_DIGEST_LENGTH]; + + /** + * The keys used to encrypt/decrypt the confirm message + */ + uint8_t zrtpKeyI[MAX_DIGEST_LENGTH]; + uint8_t zrtpKeyR[MAX_DIGEST_LENGTH]; + + /** + * Pointers to negotiated hash and HMAC functions + */ + void (*hashFunction)(unsigned char *data, + unsigned int data_length, + unsigned char *digest); + + void (*hashListFunction)(unsigned char *data[], + unsigned int data_length[], + unsigned char *digest); + + void (*hmacFunction)(uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length); + + void (*hmacListFunction)( uint8_t* key, uint32_t key_length, + uint8_t* data[], uint32_t data_length[], + uint8_t* mac, uint32_t* mac_length ); + + void* (*createHashCtx)(); + + void (*closeHashCtx)(void* ctx, unsigned char* digest); + + void (*hashCtxFunction)(void* ctx, unsigned char* data, + unsigned int dataLength); + + void (*hashCtxListFunction)(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]); + + int32_t hashLength; + + // Funtion pointers to implicit hash and hmac functions + void (*hashFunctionImpl)(unsigned char *data, + unsigned int data_length, + unsigned char *digest); + + void (*hashListFunctionImpl)(unsigned char *data[], + unsigned int data_length[], + unsigned char *digest); + + void (*hmacFunctionImpl)(uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length); + + void (*hmacListFunctionImpl)( uint8_t* key, uint32_t key_length, + uint8_t* data[], uint32_t data_length[], + uint8_t* mac, uint32_t* mac_length ); + + int32_t hashLengthImpl; + + /** + * The ZRTP Session Key + * Refer to chapter 5.4.1.4 + */ + uint8_t zrtpSession[MAX_DIGEST_LENGTH]; + + /** + * True if this ZRTP instance uses multi-stream mode. + */ + bool multiStream; + + /** + * True if the other ZRTP client supports multi-stream mode. + */ + bool multiStreamAvailable; + + /** + * Enable MitM (PBX) enrollment + * + * If set to true then ZRTP honors the PBX enrollment flag in + * Commit packets and calls the appropriate user callback + * methods. If the parameter is set to false ZRTP ignores the PBX + * enrollment flags. + */ + bool enableMitmEnrollment; + + /** + * True if a valid trusted MitM key of the other peer is available, i.e. enrolled. + */ + bool peerIsEnrolled; + + /** + * Set to true if the Hello packet contained the M-flag (MitM flag). + * We use this later to check some stuff for SAS Relay processing + */ + bool mitmSeen; + + /** + * Temporarily store computed pbxSecret, if user accepts enrollment then + * it will copied to our ZID record of the PBX (MitM) + */ + uint8_t* pbxSecretTmp; + uint8_t pbxSecretTmpBuffer[MAX_DIGEST_LENGTH]; + + /** + * If true then we will set the enrollment flag (E) in the confirm + * packets. Set to true if the PBX enrollment service started this ZRTP + * session. Can be set to true only if mitmMode is also true. + */ + bool enrollmentMode; + + /** + * Configuration data which algorithms to use. + */ + ZrtpConfigure configureAlgos; + /** + * Pre-initialized packets. + */ + ZrtpPacketHello zrtpHello; + ZrtpPacketHelloAck zrtpHelloAck; + ZrtpPacketConf2Ack zrtpConf2Ack; + ZrtpPacketClearAck zrtpClearAck; + ZrtpPacketGoClear zrtpGoClear; + ZrtpPacketError zrtpError; + ZrtpPacketErrorAck zrtpErrorAck; + ZrtpPacketDHPart zrtpDH1; + ZrtpPacketDHPart zrtpDH2; + ZrtpPacketCommit zrtpCommit; + ZrtpPacketConfirm zrtpConfirm1; + ZrtpPacketConfirm zrtpConfirm2; + ZrtpPacketPingAck zrtpPingAck; + ZrtpPacketSASrelay zrtpSasRelay; + ZrtpPacketRelayAck zrtpRelayAck; + + /** + * Random IV data to encrypt the confirm data, 128 bit for AES + */ + uint8_t randomIV[16]; + + uint8_t tempMsgBuffer[1024]; + int32_t lengthOfMsgData; + + /** + * Variables to store signature data. Includes the signature type block + */ + const uint8_t* signatureData; // will be set when needed + int32_t signatureLength; // overall length in bytes + + /** + * Is true if the other peer signaled SAS signature support in its Hello packet. + */ + bool signSasSeen; + + uint32_t peerSSRC; // peer's SSRC, required to setup PingAck packet + + /** + * Enable or disable paranoid mode. + * + * The Paranoid mode controls the behaviour and handling of the SAS verify flag. If + * Panaoid mode is set to flase then ZRtp applies the normal handling. If Paranoid + * mode is set to true then the handling is: + * + * <ul> + * <li> Force the SAS verify flag to be false at srtpSecretsOn() callback. This gives + * the user interface (UI) the indication to handle the SAS as <b>not verified</b>. + * See implementation note below.</li> + * <li> Don't set the SAS verify flag in the <code>Confirm</code> packets, thus the other + * also must report the SAS as <b>not verified</b>.</li> + * <li> ignore the <code>SASVerified()</code> function, thus do not set the SAS to verified + * in the ZRTP cache. </li> + * <li> Disable the <b>Trusted PBX MitM</b> feature. Just send the <code>SASRelay</code> packet + * but do not process the relayed data. This protects the user from a malicious + * "trusted PBX".</li> + * </ul> + * ZRtp performs alls other steps during the ZRTP negotiations as usual, in particular it + * computes, compares, uses, and stores the retained secrets. This avoids unnecessary warning + * messages. The user may enable or disable the Paranoid mode on a call-by-call basis without + * breaking the key continuity data. + * + * <b>Implementation note:</b></br> + * An application shall always display the SAS code if the SAS verify flag is <code>false</code>. + * The application shall also use mechanisms to remind the user to compare the SAS code, for + * example useing larger fonts, different colours and other display features. + */ + bool paranoidMode; + + /** + * Find the best Hash algorithm that is offered in Hello. + * + * Find the best, that is the strongest, Hash algorithm that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @return + * The Enum that identifies the best offered Hash algortihm. Return + * <code>NumSupportedHashes</code> to signal that no matching Hash algorithm + * was found at all. + */ + AlgorithmEnum* findBestHash(ZrtpPacketHello *hello); + + /** + * Find the best symmetric cipher algorithm that is offered in Hello. + * + * Find the best, that is the strongest, cipher algorithm that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @param pk + * The id of the selected public key algorithm + * @return + * The Enum that identifies the best offered Cipher algortihm. Return + * <code>NumSupportedSymCiphers</code> to signal that no matching Cipher algorithm + * was found at all. + */ + AlgorithmEnum* findBestCipher(ZrtpPacketHello *hello, AlgorithmEnum* pk); + + /** + * Find the best Public Key algorithm that is offered in Hello. + * + * Find the best, that is the strongest, public key algorithm that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @return + * The Enum that identifies the best offered Public Key algortihm. Return + * <code>NumSupportedPubKeys</code> to signal that no matching Public Key algorithm + * was found at all. + */ + AlgorithmEnum* findBestPubkey(ZrtpPacketHello *hello); + + /** + * Find the best SAS algorithm that is offered in Hello. + * + * Find the best, that is the strongest, SAS algorithm that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @return + * The Enum that identifies the best offered SAS algortihm. Return + * <code>NumSupportedSASTypes</code> to signal that no matching SAS algorithm + * was found at all. + */ + AlgorithmEnum* findBestSASType(ZrtpPacketHello* hello); + + /** + * Find the best authentication length that is offered in Hello. + * + * Find the best, that is the strongest, authentication length that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @return + * The Enum that identifies the best offered authentication length. Return + * <code>NumSupportedAuthLenghts</code> to signal that no matching length + * was found at all. + */ + AlgorithmEnum* findBestAuthLen(ZrtpPacketHello* hello); + + /** + * Check if MultiStream mode is offered in Hello. + * + * Find the best, that is the strongest, authentication length that our peer + * offers in its Hello packet. + * + * @param hello + * The Hello packet. + * @return + * True if multi stream mode is available, false otherwise. + */ + bool checkMultiStream(ZrtpPacketHello* hello); + + /** + * Save the computed MitM secret to the ZID record of the peer + */ + void writeEnrollmentPBX(); + + /** + * Compute my hvi value according to ZRTP specification. + */ + void computeHvi(ZrtpPacketDHPart* dh, ZrtpPacketHello *hello); + + void computeSharedSecretSet(ZIDRecord& zidRec); + + void computeSRTPKeys(); + + void KDF(uint8_t* key, uint32_t keyLength, uint8_t* label, int32_t labelLength, + uint8_t* context, int32_t contextLength, int32_t L, uint8_t* output); + + void generateKeysInitiator(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec); + + void generateKeysResponder(ZrtpPacketDHPart *dhPart, ZIDRecord& zidRec); + + void generateKeysMultiStream(); + + void computePBXSecret(); + + void setNegotiatedHash(AlgorithmEnum* hash); + + /* + * The following methods are helper functions for ZrtpStateClass. + * ZrtpStateClass calls them to prepare packets, send data, report + * problems, etc. + */ + /** + * Send a ZRTP packet. + * + * The state engines calls this method to send a packet via the RTP + * stack. + * + * @param packet + * Points to the ZRTP packet. + * @return + * zero if sending failed, one if packet was send + */ + int32_t sendPacketZRTP(ZrtpPacketBase *packet); + + /** + * Activate a Timer using the host callback. + * + * @param tm + * The time in milliseconds. + * @return + * zero if activation failed, one if timer was activated + */ + int32_t activateTimer(int32_t tm); + + /** + * Cancel the active Timer using the host callback. + * + * @return + * zero if activation failed, one if timer was activated + */ + int32_t cancelTimer(); + + /** + * Prepare a Hello packet. + * + * Just take the preinitialized Hello packet and return it. No + * further processing required. + * + * @return + * A pointer to the initialized Hello packet. + */ + ZrtpPacketHello* prepareHello(); + + /** + * Prepare a HelloAck packet. + * + * Just take the preinitialized HelloAck packet and return it. No + * further processing required. + * + * @return + * A pointer to the initialized HelloAck packet. + */ + ZrtpPacketHelloAck* prepareHelloAck(); + + /** + * Prepare a Commit packet. + * + * We have received a Hello packet from our peer. Check the offers + * it makes to us and select the most appropriate. Using the + * selected values prepare a Commit packet and return it to protocol + * state engine. + * + * @param hello + * Points to the received Hello packet + * @param errMsg + * Points to an integer that can hold a ZRTP error code. + * @return + * A pointer to the prepared Commit packet + */ + ZrtpPacketCommit* prepareCommit(ZrtpPacketHello *hello, uint32_t* errMsg); + + /** + * Prepare a Commit packet for Multi Stream mode. + * + * Using the selected values prepare a Commit packet and return it to protocol + * state engine. + * + * @param hello + * Points to the received Hello packet + * @return + * A pointer to the prepared Commit packet for multi stream mode + */ + ZrtpPacketCommit* prepareCommitMultiStream(ZrtpPacketHello *hello); + + /** + * Prepare the DHPart1 packet. + * + * This method prepares a DHPart1 packet. The input to the method is always + * a Commit packet received from the peer. Also we a in the role of the + * Responder. + * + * When we receive a Commit packet we get the selected ciphers, hashes, etc + * and cross-check if this is ok. Then we need to initialize a set of DH + * keys according to the selected cipher. Using this data we prepare our DHPart1 + * packet. + */ + ZrtpPacketDHPart* prepareDHPart1(ZrtpPacketCommit *commit, uint32_t* errMsg); + + /** + * Prepare the DHPart2 packet. + * + * This method prepares a DHPart2 packet. The input to the method is always + * a DHPart1 packet received from the peer. Our peer sends the DH1Part as + * response to our Commit packet. Thus we are in the role of the + * Initiator. + * + */ + ZrtpPacketDHPart* prepareDHPart2(ZrtpPacketDHPart* dhPart1, uint32_t* errMsg); + + /** + * Prepare the Confirm1 packet. + * + * This method prepare the Confirm1 packet. The input to this method is the + * DHPart2 packect received from our peer. The peer sends the DHPart2 packet + * as response of our DHPart1. Here we are in the role of the Responder + * + */ + ZrtpPacketConfirm* prepareConfirm1(ZrtpPacketDHPart* dhPart2, uint32_t* errMsg); + + /** + * Prepare the Confirm1 packet in multi stream mode. + * + * This method prepares the Confirm1 packet. The state engine call this method + * if multi stream mode is selected and a Commit packet was received. The input to + * this method is the Commit. + * Here we are in the role of the Responder + * + */ + ZrtpPacketConfirm* prepareConfirm1MultiStream(ZrtpPacketCommit* commit, uint32_t* errMsg); + + /** + * Prepare the Confirm2 packet. + * + * This method prepare the Confirm2 packet. The input to this method is the + * Confirm1 packet received from our peer. The peer sends the Confirm1 packet + * as response of our DHPart2. Here we are in the role of the Initiator + */ + ZrtpPacketConfirm* prepareConfirm2(ZrtpPacketConfirm* confirm1, uint32_t* errMsg); + + /** + * Prepare the Confirm2 packet in multi stream mode. + * + * This method prepares the Confirm2 packet. The state engine call this method if + * multi stream mode is active and in state CommitSent. The input to this method is + * the Confirm1 packet received from our peer. The peer sends the Confirm1 packet + * as response of our Commit packet in multi stream mode. + * Here we are in the role of the Initiator + */ + ZrtpPacketConfirm* prepareConfirm2MultiStream(ZrtpPacketConfirm* confirm1, uint32_t* errMsg); + + /** + * Prepare the Conf2Ack packet. + * + * This method prepare the Conf2Ack packet. The input to this method is the + * Confirm2 packet received from our peer. The peer sends the Confirm2 packet + * as response of our Confirm1. Here we are in the role of the Initiator + */ + ZrtpPacketConf2Ack* prepareConf2Ack(ZrtpPacketConfirm* confirm2, uint32_t* errMsg); + + /** + * Prepare the ErrorAck packet. + * + * This method prepares the ErrorAck packet. The input to this method is the + * Error packet received from the peer. + */ + ZrtpPacketErrorAck* prepareErrorAck(ZrtpPacketError* epkt); + + /** + * Prepare the Error packet. + * + * This method prepares the Error packet. The input to this method is the + * error code to be included into the message. + */ + ZrtpPacketError* prepareError(uint32_t errMsg); + + /** + * Prepare a ClearAck packet. + * + * This method checks if the GoClear message is valid. If yes then switch + * off SRTP processing, stop sending of RTP packets (pause transmit) and + * inform the user about the fact. Only if user confirms the GoClear message + * normal RTP processing is resumed. + * + * @return + * NULL if GoClear could not be authenticated, a ClearAck packet + * otherwise. + */ + ZrtpPacketClearAck* prepareClearAck(ZrtpPacketGoClear* gpkt); + + /** + * Prepare the ErrorAck packet. + * + * This method prepares the ErrorAck packet. The input to this method is the + * Error packet received from the peer. + */ + ZrtpPacketPingAck* preparePingAck(ZrtpPacketPing* ppkt); + + /** + * Prepare the RelayAck packet. + * + * This method prepares the RelayAck packet. The input to this method is the + * SASrelay packet received from the peer. + */ + ZrtpPacketRelayAck* prepareRelayAck(ZrtpPacketSASrelay* srly, uint32_t* errMsg); + + /** + * Prepare a GoClearAck packet w/o HMAC + * + * Prepare a GoCLear packet without a HMAC but with a short error message. + * This type of GoClear is used if something went wrong during the ZRTP + * negotiation phase. + * + * @return + * A goClear packet without HMAC + */ + ZrtpPacketGoClear* prepareGoClear(uint32_t errMsg = 0); + + /** + * Compare the hvi values. + * + * Compare a received Commit packet with our Commit packet and returns + * which Commit packt is "more important". See chapter 5.2 to get further + * information how to compare Commit packets. + * + * @param commit + * Pointer to the peer's commit packet we just received. + * @return + * <0 if our Commit packet is "less important" + * >0 if our Commit is "more important" + * 0 shouldn't happen because we compare crypto hashes + */ + int32_t compareCommit(ZrtpPacketCommit *commit); + + /** + * Verify the H2 hash image. + * + * Verifies the H2 hash contained in a received commit message. + * This functions just verifies H2 but does not store it. + * + * @param commit + * Pointer to the peer's commit packet we just received. + * @return + * true if H2 is ok and verified + * false if H2 could not be verified + */ + bool verifyH2(ZrtpPacketCommit *commit); + + /** + * Send information messages to the hosting environment. + * + * The ZRTP implementation uses this method to send information messages + * to the host. Along with the message ZRTP provides a severity indicator + * that defines: Info, Warning, Error, Alert. Refer to the MessageSeverity + * enum in the ZrtpCallback class. + * + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + void sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); + + /** + * ZRTP state engine calls this if the negotiation failed. + * + * ZRTP calls this method in case ZRTP negotiation failed. The parameters + * show the severity as well as some explanatory text. + * + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); + + /** + * ZRTP state engine calls this method if the other side does not support ZRTP. + * + * If the other side does not answer the ZRTP <em>Hello</em> packets then + * ZRTP calls this method, + * + */ + void zrtpNotSuppOther(); + + /** + * Signal SRTP secrets are ready. + * + * This method calls a callback method to inform the host that the SRTP + * secrets are ready. + * + * @param part + * Defines for which part (sender or receiver) to switch on security + * @return + * Returns false if something went wrong during initialization of SRTP + * context. Propagate error back to state engine. + */ + bool srtpSecretsReady(EnableSecurity part); + + /** + * Switch off SRTP secrets. + * + * This method calls a callback method to inform the host that the SRTP + * secrets shall be cleared. + * + * @param part + * Defines for which part (sender or receiver) to clear + */ + void srtpSecretsOff(EnableSecurity part); + + /** + * ZRTP state engine calls these methods to enter or leave its + * synchronization mutex. + */ + void synchEnter(); + + void synchLeave(); + + /** + * Helper function to store ZRTP message data in a temporary buffer + * + * This functions first clears the temporary buffer, then stores + * the packet's data to it. We use this to check the packet's HMAC + * after we received the HMAC key in to following packet. + * + * @param data + * Pointer to the packet's ZRTP message + */ + void storeMsgTemp(ZrtpPacketBase* pkt); + + /** + * Helper function to check a ZRTP message HMAC + * + * This function gets a HMAC key and uses it to compute a HMAC + * with this key and the stored data of a previous received ZRTP + * message. It compares the computed HMAC and the HMAC stored in + * the received message and returns the result. + * + * @param key + * Pointer to the HMAC key. + * @return + * Returns true if the computed HMAC and the stored HMAC match, + * false otherwise. + */ + bool checkMsgHmac(uint8_t* key); + + /** + * Set the client ID for ZRTP Hello message. + * + * The user of ZRTP must set its id to identify itself in the + * ZRTP HELLO message. The maximum length is 16 characters. Shorter + * id string are allowed, they will be filled with blanks. A longer id + * is truncated to 16 characters. + * + * The identifier is set in the Hello packet of ZRTP. Thus only after + * setting the identifier ZRTP can compute the HMAC and the final + * helloHash. + * + * @param id + * The client's id + */ + void setClientId(std::string id); +}; + +/** + * @} + */ +#endif // ZRTP + diff --git a/src/libzrtpcpp/ZrtpCWrapper.h b/src/libzrtpcpp/ZrtpCWrapper.h new file mode 100644 index 0000000..0126545 --- /dev/null +++ b/src/libzrtpcpp/ZrtpCWrapper.h @@ -0,0 +1,1339 @@ +/* + This file defines the GNU ZRTP C-to-C++ wrapper. + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef ZRTPCWRAPPER_H +#define ZRTPCWRAPPER_H + +/** + * + * @file ZrtpCWrapper.h + * @brief The GNU ZRTP C-to-C++ wrapper. + * + * To avoid any include of C++ header files some structure, defines, and + * enumerations are repeated in this file. Refer to the inline comments if + * you modify the file. + * + * @ingroup GNU_ZRTP + * @{ + * + * @see ZRtp + */ + +#include <stdint.h> + +/** + * Defines to specify the role a ZRTP peer has. + * + * According to the ZRTP specification the role determines which keys to + * use to encrypt or decrypt SRTP data. + * + * <ul> + * <li> The Initiator encrypts SRTP data using the <em>keyInitiator</em> and the + * <em>saltInitiator</em> data, the Responder uses these data to decrypt. + * </li> + * <li> The Responder encrypts SRTP data using the <em>keyResponder</em> and the + * <em>saltResponder</em> data, the Initiator uses these data to decrypt. + * </li> + * </ul> + */ +/* + * Keep the following defines in sync with Role enumeration in ZrtpCallback.h + */ +#define Responder 1 /*!< This client is in ZRTP Responder mode */ +#define Initiator 2 /*!< This client is in ZRTP Initiator mode */ + +#define CRC_SIZE 4 /*!< Size of CRC code of a ZRTP packet */ +#define ZRTP_MAGIC 0x5a525450 /*!< The magic code that identifies a ZRTP packet */ +#define MAX_ZRTP_SIZE 3072 /*!< The biggest ZRTP packet ever possible */ + +/* + * IMPORTANT: keep the following enums in synch with ZrtpCodes. We copy them here + * to avoid any C++ header includes and defines. The protocol states are located + * ZrtpStateClass.h . + */ +/** + * This enum defines the information message severity. + * + * The ZRTP implementation issues information messages to inform the user + * about ongoing processing, unusual behavior, or alerts in case of severe + * problems. Each main severity code a number of sub-codes exist that + * specify the exact nature of the problem. + * + * An application gets message severity codes and the associated sub-codes + * via the ZrtpUserCallback#showMessage method. + * + * The severity levels and their meaning are: + * + * <dl> + * <dt>Info</dt> <dd>keeps the user informed about ongoing processing and + * security setup. The enumeration InfoCodes defines the subcodes. + * </dd> + * <dt>Warning</dt> <dd>is an information about some security issues, e.g. if + * an AES 256 encryption is request but only DH 3072 as public key scheme + * is supported. ZRTP will establish a secure session (SRTP). The + * enumeration WarningCodes defines the sub-codes. + * </dd> + * <dt>Severe</dt> <dd>is used if an error occured during ZRTP protocol usage. + * In case of <em>Severe</em> ZRTP will <b>not</b> establish a secure session. + * The enumeration SevereCodes defines the sub-codes. + * </dd> + * <dt>Zrtp</dt> <dd>shows a ZRTP security problem. Refer to the enumeration + * ZrtpErrorCodes for sub-codes. GNU ZRTP of course will <b>not</b> + * establish a secure session. + * </dd> + * </dl> + * + */ +enum zrtp_MessageSeverity { + zrtp_Info = 1, /*!< Just an info message */ + zrtp_Warning, /*!< A Warning message - security can be established */ + zrtp_Severe, /*!< Severe error, security will not be established */ + zrtp_ZrtpError /*!< ZRTP error, security will not be established */ +}; + +/** + * Sub-codes for Info + */ +enum zrtp_InfoCodes { + zrtp_InfoHelloReceived = 1, /*!< Hello received, preparing a Commit */ + zrtp_InfoCommitDHGenerated, /*!< Commit: Generated a public DH key */ + zrtp_InfoRespCommitReceived, /*!< Responder: Commit received, preparing DHPart1 */ + zrtp_InfoDH1DHGenerated, /*!< DH1Part: Generated a public DH key */ + zrtp_InfoInitDH1Received, /*!< Initiator: DHPart1 received, preparing DHPart2 */ + zrtp_InfoRespDH2Received, /*!< Responder: DHPart2 received, preparing Confirm1 */ + zrtp_InfoInitConf1Received, /*!< Initiator: Confirm1 received, preparing Confirm2 */ + zrtp_InfoRespConf2Received, /*!< Responder: Confirm2 received, preparing Conf2Ack */ + zrtp_InfoRSMatchFound, /*!< At least one retained secrets matches - security OK */ + zrtp_InfoSecureStateOn, /*!< Entered secure state */ + zrtp_InfoSecureStateOff /*!< No more security for this session */ +}; + +/** + * Sub-codes for Warning + */ +enum zrtp_WarningCodes { + zrtp_WarningDHAESmismatch = 1, /*!< Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096 */ + zrtp_WarningGoClearReceived, /*!< Received a GoClear message */ + zrtp_WarningDHShort, /*!< Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096 */ + zrtp_WarningNoRSMatch, /*!< No retained shared secrets available - must verify SAS */ + zrtp_WarningCRCmismatch, /*!< Internal ZRTP packet checksum mismatch - packet dropped */ + zrtp_WarningSRTPauthError, /*!< Dropping packet because SRTP authentication failed! */ + zrtp_WarningSRTPreplayError, /*!< Dropping packet because SRTP replay check failed! */ + zrtp_WarningNoExpectedRSMatch /*!< Valid retained shared secrets availabe but no matches found - must verify SAS */ +}; + +/** + * Sub-codes for Severe + */ +enum zrtp_SevereCodes { + zrtp_SevereHelloHMACFailed = 1, /*!< Hash HMAC check of Hello failed! */ + zrtp_SevereCommitHMACFailed, /*!< Hash HMAC check of Commit failed! */ + zrtp_SevereDH1HMACFailed, /*!< Hash HMAC check of DHPart1 failed! */ + zrtp_SevereDH2HMACFailed, /*!< Hash HMAC check of DHPart2 failed! */ + zrtp_SevereCannotSend, /*!< Cannot send data - connection or peer down? */ + zrtp_SevereProtocolError, /*!< Internal protocol error occured! */ + zrtp_SevereNoTimer, /*!< Cannot start a timer - internal resources exhausted? */ + zrtp_SevereTooMuchRetries /*!< Too much retries during ZRTP negotiation - connection or peer down? */ +}; + +/** + * Error codes according to the ZRTP specification chapter 6.9 + * + * GNU ZRTP uses these error codes in two ways: to fill the appropriate + * field ing the ZRTP Error packet and as sub-code in + * ZrtpUserCallback#showMessage(). GNU ZRTP uses thes error codes also + * to report received Error packts, in this case the sub-codes are their + * negative values. + * + * The enumeration member comments are copied from the ZRTP specification. + */ +enum zrtp_ZrtpErrorCodes { + zrtp_MalformedPacket = 0x10, /*!< Malformed packet (CRC OK, but wrong structure) */ + zrtp_CriticalSWError = 0x20, /*!< Critical software error */ + zrtp_UnsuppZRTPVersion = 0x30, /*!< Unsupported ZRTP version */ + zrtp_HelloCompMismatch = 0x40, /*!< Hello components mismatch */ + zrtp_UnsuppHashType = 0x51, /*!< Hash type not supported */ + zrtp_UnsuppCiphertype = 0x52, /*!< Cipher type not supported */ + zrtp_UnsuppPKExchange = 0x53, /*!< Public key exchange not supported */ + zrtp_UnsuppSRTPAuthTag = 0x54, /*!< SRTP auth. tag not supported */ + zrtp_UnsuppSASScheme = 0x55, /*!< SAS scheme not supported */ + zrtp_NoSharedSecret = 0x56, /*!< No shared secret available, DH mode required */ + zrtp_DHErrorWrongPV = 0x61, /*!< DH Error: bad pvi or pvr ( == 1, 0, or p-1) */ + zrtp_DHErrorWrongHVI = 0x62, /*!< DH Error: hvi != hashed data */ + zrtp_SASuntrustedMiTM = 0x63, /*!< Received relayed SAS from untrusted MiTM */ + zrtp_ConfirmHMACWrong = 0x70, /*!< Auth. Error: Bad Confirm pkt HMAC */ + zrtp_NonceReused = 0x80, /*!< Nonce reuse */ + zrtp_EqualZIDHello = 0x90, /*!< Equal ZIDs in Hello */ + zrtp_GoCleatNotAllowed = 0x100, /*!< GoClear packet received, but not allowed */ + zrtp_IgnorePacket = 0x7fffffff /*!< Internal state, not reported */ +}; + +/** + * Information codes for the Enrollment user callbacks. + */ +enum zrtp_InfoEnrollment { + zrtp_EnrollmentRequest, //!< Aks user to confirm or deny an Enrollemnt request + zrtp_EnrollmentCanceled, //!< User did not confirm the PBX enrollement + zrtp_EnrollmentFailed, //!< Enrollment process failed, no PBX secret available + zrtp_EnrollmentOk //!< Enrollment process for this PBX was ok +}; + +/* The ZRTP protocol states */ +enum zrtpStates { + Initial, /*!< Initial state after starting the state engine */ + Detect, /*!< State sending Hello, try to detect answer message */ + AckDetected, /*!< HelloAck received */ + AckSent, /*!< HelloAck sent after Hello received */ + WaitCommit, /*!< Wait for a Commit message */ + CommitSent, /*!< Commit message sent */ + WaitDHPart2, /*!< Wait for a DHPart2 message */ + WaitConfirm1, /*!< Wait for a Confirm1 message */ + WaitConfirm2, /*!< Wait for a confirm2 message */ + WaitConfAck, /*!< Wait for Conf2Ack */ + WaitClearAck, /*!< Wait for clearAck - not used */ + SecureState, /*!< This is the secure state - SRTP active */ + WaitErrorAck, /*!< Wait for ErrorAck message */ + numberOfStates /*!< Gives total number of protocol states */ +}; + +/*! The algorihms that we support in SRTP and that ZRTP can negotiate. */ +typedef enum { + zrtp_Aes = 1, /*!< Use AES as symmetrical cipher algorithm */ + zrtp_TwoFish, /*!< Use TwoFish as symmetrical cipher algorithm */ + zrtp_Sha1, /*!< Use Sha1 as authentication algorithm */ + zrtp_Skein /*!< Use Skein as authentication algorithm */ +} zrtp_SrtpAlgorithms; + +/** + * This structure contains pointers to the SRTP secrets and the role info. + * + * About the role and what the meaning of the role is refer to the + * of the enum Role. The pointers to the secrets are valid as long as + * the ZRtp object is active. To use these data after the ZRtp object's + * lifetime you may copy the data into a save place. + */ +typedef struct c_srtpSecrets +{ + zrtp_SrtpAlgorithms symEncAlgorithm;/*!< symmetrical cipher algorithm */ + const uint8_t* keyInitiator; /*!< Initiator's key */ + int32_t initKeyLen; /*!< Initiator's key length */ + const uint8_t* saltInitiator; /*!< Initiator's salt */ + int32_t initSaltLen; /*!< Initiator's salt length */ + const uint8_t* keyResponder; /*!< Responder's key */ + int32_t respKeyLen; /*!< Responder's key length */ + const uint8_t* saltResponder; /*!< Responder's salt */ + int32_t respSaltLen; /*!< Responder's salt length */ + zrtp_SrtpAlgorithms authAlgorithm; /*!< SRTP authentication algorithm */ + int32_t srtpAuthTagLen; /*!< SRTP authentication length */ + char* sas; /*!< The SAS string */ + int32_t role; /*!< ZRTP role of this client */ +} C_SrtpSecret_t; + +/* + * Keep the following defines in sync with enum EnableSecurity in ZrtpCallback.h + */ +#define ForReceiver 1 /*!< Enable security for SRTP receiver */ +#define ForSender 2 /*!< Enable security for SRTP sender */ + +#ifdef __cplusplus +#pragma GCC visibility push(default) +extern "C" +{ +#endif + + typedef struct ZRtp ZRtp; + typedef struct ZrtpCallbackWrapper ZrtpCallbackWrapper; + typedef struct ZrtpConfigure ZrtpConfigure; + + + typedef struct zrtpContext + { + ZRtp* zrtpEngine; /*!< Holds the real ZRTP engine */ + ZrtpCallbackWrapper* zrtpCallback; /*!< Help class Callback wrapper */ + ZrtpConfigure* configure; /*!< Optional configuration data */ + void* userData; /*!< User data, set by application */ + } ZrtpContext; + + /** + * This structure defines the callback functions required by GNU ZRTP. + * + * The RTP stack specific part must implement the callback methods. + * The generic part of GNU ZRTP uses these mehtods + * to communicate with the specific part, for example to send data + * via the RTP/SRTP stack, to set timers and cancel timer and so on. + * + * The generiy part of GNU ZRTP needs only a few callback methods to + * be implemented by the specific part. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + + /** + * The following methods define the GNU ZRTP callback interface. + * For detailed documentation refer to file ZrtpCallback.h, each C + * method has "zrtp_" prepended to the C++ name. + * + * @see ZrtpCallback + */ + typedef struct zrtp_Callbacks + { + /** + * Send a ZRTP packet via RTP. + * + * ZRTP calls this method to send a ZRTP packet via the RTP session. + * The ZRTP packet will have to be created using the provided ZRTP message. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param data + * Points to ZRTP message to send. + * @param length + * The length in bytes of the data + * @return + * zero if sending failed, one if packet was sent + */ + int32_t (*zrtp_sendDataZRTP) (ZrtpContext* ctx, const uint8_t* data, int32_t length ) ; + + /** + * Activate timer. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param time + * The time in ms for the timer + * @return + * zero if activation failed, one if timer was activated + */ + int32_t (*zrtp_activateTimer) (ZrtpContext* ctx, int32_t time ) ; + + /** + * Cancel the active timer. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @return + * zero if cancel action failed, one if timer was canceled + */ + int32_t (*zrtp_cancelTimer)(ZrtpContext* ctx) ; + + /** + * Send information messages to the hosting environment. + * + * The ZRTP implementation uses this method to send information + * messages to the host. Along with the message ZRTP provides a + * severity indicator that defines: Info, Warning, Error, + * Alert. Refer to the <code>MessageSeverity</code> enum above. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + void (*zrtp_sendInfo) (ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; + + /** + * SRTP crypto data ready for the sender or receiver. + * + * The ZRTP implementation calls this method right after all SRTP + * secrets are computed and ready to be used. The parameter points + * to a structure that contains pointers to the SRTP secrets and a + * <code>enum Role</code>. The called method (the implementation + * of this abstract method) must either copy the pointers to the SRTP + * data or the SRTP data itself to a save place. The SrtpSecret_t + * structure is destroyed after the callback method returns to the + * ZRTP implementation. + * + * The SRTP data themselves are obtained in the ZRtp object and are + * valid as long as the ZRtp object is active. TheZRtp's + * destructor clears the secrets. Thus the called method needs to + * save the pointers only, ZRtp takes care of the data. + * + * The implementing class may enable SRTP processing in this + * method or delay it to srtpSecertsOn(). + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param secrets A pointer to a SrtpSecret_t structure that + * contains all necessary data. + * + * @param part for which part (Sender or Receiver) this data is + * valid. + * + * @return Returns false if something went wrong during + * initialization of SRTP context, for example memory shortage. + */ + int32_t (*zrtp_srtpSecretsReady) (ZrtpContext* ctx, C_SrtpSecret_t* secrets, int32_t part ) ; + + /** + * Switch off the security for the defined part. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param part Defines for which part (sender or receiver) to + * switch on security + */ + void (*zrtp_srtpSecretsOff) (ZrtpContext* ctx, int32_t part ) ; + + /** + * Switch on the security. + * + * ZRTP calls this method after it has computed the SAS and check + * if it is verified or not. In addition ZRTP provides information + * about the cipher algorithm and key length for the SRTP session. + * + * This method must enable SRTP processing if it was not enabled + * during sertSecretsReady(). + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param c The name of the used cipher algorithm and mode, or + * NULL + * + * @param s The SAS string + * + * @param verified if <code>verified</code> is true then SAS was + * verified by both parties during a previous call. + */ + void (*zrtp_rtpSecretsOn) (ZrtpContext* ctx, char* c, char* s, int32_t verified ) ; + + /** + * This method handles GoClear requests. + * + * According to the ZRTP specification the user must be informed about + * a GoClear request because the ZRTP implementation switches off security + * if it could authenticate the GoClear packet. + * + * <b>Note:</b> GoClear is not yet implemented in GNU ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + */ + void (*zrtp_handleGoClear)(ZrtpContext* ctx) ; + + /** + * Handle ZRTP negotiation failed. + * + * ZRTP calls this method in case ZRTP negotiation failed. The + * parameters show the severity as well as the reason. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + void (*zrtp_zrtpNegotiationFailed) (ZrtpContext* ctx, int32_t severity, int32_t subCode ) ; + + /** + * ZRTP calls this method if the other side does not support ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * If the other side does not answer the ZRTP <em>Hello</em> packets then + * ZRTP calls this method, + * + */ + void (*zrtp_zrtpNotSuppOther)(ZrtpContext* ctx) ; + + /** + * Enter synchronization mutex. + * + * GNU ZRTP requires one mutex to synchronize its + * processing. Because mutex implementations depend on the + * underlying infrastructure, for example operating system or + * thread implementation, GNU ZRTP delegates mutex handling to the + * specific part of its implementation. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + */ + void (*zrtp_synchEnter)(ZrtpContext* ctx) ; + + /** + * Leave synchronization mutex. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + */ + void (*zrtp_synchLeave)(ZrtpContext* ctx) ; + + /** + * Inform about a PBX enrollment request. + * + * Please refer to chapter 8.3 ff to get more details about PBX + * enrollment and SAS relay. + * + * <b>Note:</b> PBX enrollement is not yet fully supported by GNU + * ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param info Give some information to the user about the PBX + * requesting an enrollment. + */ + void (*zrtp_zrtpAskEnrollment) (ZrtpContext* ctx, int32_t info) ; + + /** + * Inform about PBX enrollment result. + * + * Informs the use about the acceptance or denial of an PBX enrollment + * request + * + * <b>Note:</b> PBX enrollement is not yet fully supported by GNU + * ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param info Give some information to the user about the result + * of an enrollment. + */ + void (*zrtp_zrtpInformEnrollment) (ZrtpContext* ctx, int32_t info ) ; + + /** + * Request a SAS signature. + * + * After ZRTP was able to compute the Short Authentication String + * (SAS) it calls this method. The client may now use an + * approriate method to sign the SAS. The client may use + * ZrtpQueue#setSignatureData() to store the signature data and + * enable signature transmission to the other peer. Refer to + * chapter 8.2 of ZRTP specification. + * + * <b>Note:</b> SAS signing is not yet fully supported by GNU + * ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param sas + * Pointer to the 32 byte SAS hash to sign. + * + */ + void (*zrtp_signSAS)(ZrtpContext* ctx, uint8_t* sas) ; + + /** + * ZRTPQueue calls this method to request a SAS signature check. + * + * After ZRTP received a SAS signature in one of the Confirm packets it + * call this method. The client may use <code>getSignatureLength()</code> + * and <code>getSignatureData()</code>of ZrtpQueue to get the signature + * data and perform the signature check. Refer to chapter 8.2 of ZRTP + * specification. + * + * If the signature check fails the client may return false to ZRTP. In + * this case ZRTP signals an error to the other peer and terminates + * the ZRTP handshake. + * + * <b>Note:</b> SAS signing is not yet fully supported by GNU + * ZRTP. + * + * @param ctx + * Pointer to the opaque ZrtpContext structure. + * @param sas + * Pointer to the 32 byte SAS hash that was signed by the other peer. + * @return + * true if the signature was ok, false otherwise. + * + */ + int32_t (*zrtp_checkSASSignature) (ZrtpContext* ctx, uint8_t* sas ) ; + } zrtp_Callbacks; + + /** + * Create the GNU ZRTP C wrapper. + * + * This wrapper implements the C interface to the C++ based GNU ZRTP. + * @returns + * Pointer to the ZrtpContext + */ + ZrtpContext* zrtp_CreateWrapper(); + + /** + * Initialize the ZRTP protocol engine. + * + * This method initialized the GNU ZRTP protocol engine. An application + * calls this method to actually create the ZRTP protocol engine and + * initialize its configuration data. This method does not start the + * protocol engine. + * + * If an application requires a specific algorithm configuration then it + * must set the algorithm configuration data before it initializes the + * ZRTP protocol engine. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param cb + * The callback structure that holds the addresses of the callback + * methods. + * @param id + * A C string that holds the ZRTP client id, only the first 16 chars + * are used. + * @param zidFilename + * The name of the ZID file. This file holds some parameters and + * other data like additional shared secrets. + * @param userData + * A pointer to user data. The wrapper just stores this pointer in + * the ZrtpContext and the application may use it for its purposes. + * @param mitmMode + * A trusted Mitm (PBX) must set this to true. The ZRTP engine sets + * the M Flag in the Hello packet to announce a trusted MitM. + * @returns + * Pointer to the ZrtpContext + * + * @see zrtp_InitializeConfig + */ + void zrtp_initializeZrtpEngine(ZrtpContext* zrtpContext, + zrtp_Callbacks *cb, + const char* id, + const char* zidFilename, + void* userData, + int32_t mitmMode); + + /** + * Destroy the ZRTP wrapper and its underlying objects. + */ + void zrtp_DestroyWrapper (ZrtpContext* zrtpContext); + + /** + * Computes the ZRTP checksum over a received ZRTP packet buffer and + * compares the result with received checksum. + * + * @param buffer + * Pointer to ZRTP packet buffer + * @param length + * Length of the packet buffer excluding received CRC data + * @param crc + * The received CRC data. + * @returns + * True if CRC matches, false otherwise. + */ + int32_t zrtp_CheckCksum(uint8_t* buffer, uint16_t length, uint32_t crc); + + /** + * Computes the ZRTP checksum over a newly created ZRTP packet buffer. + * + * @param buffer + * Pointer to the created ZRTP packet buffer + * @param length + * Length of the packet buffer + * @returns + * The computed CRC. + */ + uint32_t zrtp_GenerateCksum(uint8_t* buffer, uint16_t length); + + /** + * Prepares the ZRTP checksum for appending to ZRTP packet. + * @param crc + * The computed CRC data. + * @returns + * Prepared CRC data in host order + */ + uint32_t zrtp_EndCksum(uint32_t crc); + + /** + * Kick off the ZRTP protocol engine. + * + * This method calls the ZrtpStateClass#evInitial() state of the state + * engine. After this call we are able to process ZRTP packets + * from our peer and to process them. + * + * <b>NOTE: application shall never call this method directly but use the + * appropriate method provided by the RTP implementation. </b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_startZrtpEngine(ZrtpContext* zrtpContext); + + /** + * Stop ZRTP security. + * + * <b>NOTE: An application shall never call this method directly but use the + * appropriate method provided by the RTP implementation. </b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_stopZrtpEngine(ZrtpContext* zrtpContext); + + /** + * Process RTP extension header. + * + * This method expects to get a pointer to the message part of + * a ZRTP packet. + * + * <b>NOTE: An application shall never call this method directly. Only + * the module that implements the RTP binding shall use this method</b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param extHeader + * A pointer to the first byte of the ZRTP message part. + * @param peerSSRC + * The peer's SSRC. + * @return + * Code indicating further packet handling, see description above. + */ + void zrtp_processZrtpMessage(ZrtpContext* zrtpContext, uint8_t *extHeader, uint32_t peerSSRC); + + /** + * Process a timeout event. + * + * We got a timeout from the timeout provider. Forward it to the + * protocol state engine. + * + * <b>NOTE: application shall never call this method directly. Only + * the module that implements the RTP binding shall use this method</b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_processTimeout(ZrtpContext* zrtpContext); + + /* + * Check for and handle GoClear ZRTP packet header. + * + * This method checks if this is a GoClear packet. If not, just return + * false. Otherwise handle it according to the specification. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param extHeader + * A pointer to the first byte of the extension header. Refer to + * RFC3550. + * @return + * False if not a GoClear, true otherwise. + * + int32_t zrtp_handleGoClear(ZrtpContext* zrtpContext, uint8_t *extHeader); + */ + + /** + * Set the auxilliary secret. + * + * Use this method to set the auxilliary secret data. Refer to ZRTP + * specification, chapter 4.3 ff + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param data + * Points to the secret data. + * @param length + * Length of the auxilliary secrect in bytes + */ + void zrtp_setAuxSecret(ZrtpContext* zrtpContext, uint8_t* data, int32_t length); + + /** + * Check current state of the ZRTP state engine + * + * <b>NOTE: application usually don't call this method. Only + * the m-odule that implements the RTP binding shall use this method</b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param state + * The state to check. + * @return + * Returns true if ZRTP engine is in the given state, false otherwise. + */ + int32_t zrtp_inState(ZrtpContext* zrtpContext, int32_t state); + + /** + * Set SAS as verified. + * + * Call this method if the user confirmed (verfied) the SAS. ZRTP + * remembers this together with the retained secrets data. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_SASVerified(ZrtpContext* zrtpContext); + + /** + * Reset the SAS verfied flag for the current active user's retained secrets. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_resetSASVerified(ZrtpContext* zrtpContext); + + /** + * Get the ZRTP Hello Hash data. + * + * Use this method to get the ZRTP Hello Hash data. The method + * returns the data as a string containing the ZRTP protocol version and + * hex-digits. Refer to ZRTP specification, chapter 8. + * + * <b>NOTE: An application may call this method if it needs this information. + * Usually it is not necessary.</b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * a pointer to a C-string that contains the Hello hash value as + * hex-digits. The hello hash is available immediately after + * @c zrtp_CreateWrapper . + * The caller must @c free() if it does not use the + * hello hash C-string anymore. + */ + char* zrtp_getHelloHash(ZrtpContext* zrtpContext); + + /** + * Get the peer's ZRTP Hello Hash data. + * + * Use this method to get the peer's ZRTP Hello Hash data. The method + * returns the data as a string containing the ZRTP protocol version and + * hex-digits. + * + * The peer's hello hash is available only after ZRTP received a hello. If + * no data is available the function returns an empty string. + * + * Refer to ZRTP specification, chapter 8. + * + * @return + * a std:string containing the Hello version and the hello hash as hex digits. + */ + char* zrtp_getPeerHelloHash(ZrtpContext* zrtpContext); + + /** + * Get Multi-stream parameters. + * + * Use this method to get the Multi-stream parameters that were computed + * during the ZRTP handshake. An application may use these parameters to + * enable multi-stream processing for an associated SRTP session. + * + * The application must not modify the contents of returned char array, it + * is opaque data. The application may hand over this string to a new ZRTP + * instance to enable multi-stream processing for this new session. + * + * Refer to chapter 4.4.2 in the ZRTP specification for further details + * and restriction how and when to use multi-stream mode. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param length + * Pointer to an integer that receives the length of the char array + * @return + * a char array that contains the multi-stream parameters. + * If ZRTP was not started or ZRTP is not yet in secure state the method + * returns NULL and a length of 0. + */ + char* zrtp_getMultiStrParams(ZrtpContext* zrtpContext, int32_t *length); + + /** + * Set Multi-stream parameters. + * + * Use this method to set the parameters required to enable Multi-stream + * processing of ZRTP. The multi-stream parameters must be set before the + * application starts the ZRTP protocol engine. + * + * Refer to chapter 4.4.2 in the ZRTP specification for further details + * of multi-stream mode. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param length + * The integer that contains the length of the char array + * @param parameters + * A char array that contains the multi-stream parameters that this + * new ZRTP instance shall use. See also + * <code>getMultiStrParams()</code> + */ + void zrtp_setMultiStrParams(ZrtpContext* zrtpContext, char* parameters, int32_t length); + + /** + * Check if this ZRTP session is a Multi-stream session. + * + * Use this method to check if this ZRTP instance uses multi-stream. + * Refer to chapters 4.2 and 4.4.2 in the ZRTP. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * True if multi-stream is used, false otherwise. + */ + int32_t zrtp_isMultiStream(ZrtpContext* zrtpContext); + + /** + * Check if the other ZRTP client supports Multi-stream. + * + * Use this method to check if the other ZRTP client supports + * Multi-stream mode. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * True if multi-stream is available, false otherwise. + */ + int32_t zrtp_isMultiStreamAvailable(ZrtpContext* zrtpContext); + + /** + * Accept a PBX enrollment request. + * + * If a PBX service asks to enroll the PBX trusted MitM key and the user + * accepts this request, for example by pressing an OK button, the client + * application shall call this method and set the parameter + * <code>accepted</code> to true. If the user does not accept the request + * set the parameter to false. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param accepted + * True if the enrollment request is accepted, false otherwise. + */ + void zrtp_acceptEnrollment(ZrtpContext* zrtpContext, int32_t accepted); + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and performs the enrollment actions. A MitM (PBX) enrollment service + * started this ZRTP session. Can be set to true only if mitmMode is also true. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return status of the enrollmentMode flag. + */ + int32_t zrtp_isEnrollmentMode(ZrtpContext* zrtpContext); + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and perform the enrollment actions. A MitM (PBX) enrollment + * service must sets this mode to true. + * + * Can be set to true only if mitmMode is also true. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param enrollmentMode defines the new state of the enrollmentMode flag + */ + void zrtp_setEnrollmentMode(ZrtpContext* zrtpContext, int32_t enrollmentMode); + + /** + * Check if a peer's cache entry has a vaild MitM key. + * + * If true then the other peer ha a valid MtiM key, i.e. the peer has performed + * the enrollment procedure. A PBX ZRTP Back-2-Back application can use this function + * to check which of the peers is enrolled. + * + * @return True if the other peer has a valid Mitm key (is enrolled). + */ + int32_t isPeerEnrolled(ZrtpContext* zrtpContext); + + /** + * Send the SAS relay packet. + * + * The method creates and sends a SAS relay packet according to the ZRTP + * specifications. Usually only a MitM capable user agent (PBX) uses this + * function. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param sh the full SAS hash value + * @param render the SAS rendering algorithm + */ + int32_t zrtp_sendSASRelayPacket(ZrtpContext* zrtpContext, uint8_t* sh, char* render); + + /** + * Get the commited SAS rendering algorithm for this ZRTP session. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return the commited SAS rendering algorithm + */ + const char* zrtp_getSasType(ZrtpContext* zrtpContext); + + /** + * Get the computed SAS hash for this ZRTP session. + * + * A PBX ZRTP back-to-Back function uses this function to get the SAS + * hash of an enrolled client to construct the SAS relay packet for + * the other client. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return a pointer to the byte array that contains the full + * SAS hash. + */ + uint8_t* zrtp_getSasHash(ZrtpContext* zrtpContext); + + /** + * Set signature data + * + * This functions stores signature data and transmitts it during ZRTP + * processing to the other party as part of the Confirm packets. Refer to + * chapters 5.7 and 7.2. + * + * The signature data must be set before ZRTP the application calls + * <code>start()</code>. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param data + * The signature data including the signature type block. The method + * copies this data into the Confirm packet at signature type block. + * @param length + * The length of the signature data in bytes. This length must be + * multiple of 4. + * @return + * True if the method stored the data, false otherwise. + */ + int32_t zrtp_setSignatureData(ZrtpContext* zrtpContext, uint8_t* data, int32_t length); + + /** + * Get signature data + * + * This functions returns signature data that was receivied during ZRTP + * processing. Refer to chapters 5.7 and 7.2. + * + * The signature data can be retrieved after ZRTP enters secure state. + * <code>start()</code>. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * Number of bytes copied into the data buffer + */ + const uint8_t* zrtp_getSignatureData(ZrtpContext* zrtpContext); + + /** + * Get length of signature data + * + * This functions returns the length of signature data that was receivied + * during ZRTP processing. Refer to chapters 5.7 and 7.2. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * Length in bytes of the received signature data. The method returns + * zero if no signature data avilable. + */ + int32_t zrtp_getSignatureLength(ZrtpContext* zrtpContext); + + /** + * Emulate a Conf2Ack packet. + * + * This method emulates a Conf2Ack packet. According to ZRTP specification + * the first valid SRTP packet that the Initiator receives must switch + * on secure mode. Refer to chapter 4 in the specificaton + * + * <b>NOTE: application shall never call this method directly. Only + * the module that implements the RTP binding shall use this method</b> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_conf2AckSecure(ZrtpContext* zrtpContext); + + /** + * Get other party's ZID (ZRTP Identifier) data + * + * This functions returns the other party's ZID that was receivied + * during ZRTP processing. + * + * The ZID data can be retrieved after ZRTP receive the first Hello + * packet from the other party. The application may call this method + * for example during SAS processing in showSAS(...) user callback + * method. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param data + * Pointer to a data buffer. This buffer must have a size of + * at least 12 bytes (96 bit) (ZRTP Identifier, see chap. 4.9) + * @return + * Number of bytes copied into the data buffer - must be equivalent + * to 12 bytes. + */ + int32_t zrtp_getPeerZid(ZrtpContext* zrtpContext, uint8_t* data); + + + /** + * This enumerations list all configurable algorithm types. + */ + + /* Keep in synch with enumeration in ZrtpConfigure.h */ + + typedef enum zrtp_AlgoTypes { + zrtp_HashAlgorithm = 1, zrtp_CipherAlgorithm, zrtp_PubKeyAlgorithm, zrtp_SasType, zrtp_AuthLength + } Zrtp_AlgoTypes; + + /** + * Initialize the GNU ZRTP Configure data. + * + * Initializing and setting a ZRTP configuration is optional. GNU ZRTP + * uses a sensible default if an application does not define its own + * ZRTP configuration. + * + * If an application initialize th configure data it must set the + * configuration data. + * + * The ZRTP specification, chapters 5.1.2 through 5.1.6 defines the + * algorithm names and their meaning. + * + * The current ZRTP implementation implements all mandatory algorithms + * plus a set of the optional algorithms. An application shall use + * @c zrtp_getAlgorithmNames to get the names of the available algorithms. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @returns + * Pointer to the ZrtpConfCtx + * + * @see zrtp_getAlgorithmNames + */ + int32_t zrtp_InitializeConfig (ZrtpContext* zrtpContext); + + /** + * Get names of all available algorithmes of a given algorithm type. + * + * The algorithm names are as specified in the ZRTP specification, chapters + * 5.1.2 through 5.1.6 . + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param type + * The algorithm type. + * @returns + * A NULL terminated array of character pointers. + */ + char** zrtp_getAlgorithmNames(ZrtpContext* zrtpContext, Zrtp_AlgoTypes type); + + /** + * Free storage used to store the algorithm names. + * + * If an application does not longer require the algoritm names it should + * free the space. + * + * @param names + * The NULL terminated array of character pointers. + */ + void zrtp_freeAlgorithmNames(char** names); + + /** + * Convenience function that sets a pre-defined standard configuration. + * + * The standard configuration consists of the following algorithms: + * <ul> + * <li> Hash: SHA256 </li> + * <li> Symmetric Cipher: AES 128, AES 256 </li> + * <li> Public Key Algorithm: DH2048, DH3027, MultiStream </li> + * <li> SAS type: libase 32 </li> + * <li> SRTP Authentication lengths: 32, 80 </li> + *</ul> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_setStandardConfig(ZrtpContext* zrtpContext); + + /** + * Convenience function that sets the mandatory algorithms only. + * + * Mandatory algorithms are: + * <ul> + * <li> Hash: SHA256 </li> + * <li> Symmetric Cipher: AES 128 </li> + * <li> Public Key Algorithm: DH3027, MultiStream </li> + * <li> SAS type: libase 32 </li> + * <li> SRTP Authentication lengths: 32, 80 </li> + *</ul> + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_setMandatoryOnly(ZrtpContext* zrtpContext); + + /** + * Clear all configuration data. + * + * The functions clears all configuration data. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + */ + void zrtp_confClear(ZrtpContext* zrtpContext); + + /** + * Add an algorithm to configuration data. + * + * Adds the specified algorithm to the configuration data. + * If no free configuration data slot is available the + * function does not add the algorithm and returns -1. The + * methods appends the algorithm to the existing algorithms. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The name of the algorithm to add. + * @return + * Number of free configuration data slots or -1 on error + */ + int32_t zrtp_addAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); + + /** + * Add an algorithm to configuration data at given index + * + * Adds the specified algorithm to the configuration data vector + * at a given index. If the index is larger than the actual size + * of the configuration vector the method just appends the algorithm. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The name of the algorithm to add. + * @param index + * The index where to add the algorihm + * @return + * Number of free configuration data slots or -1 on error + */ + int32_t zrtp_addAlgoAt(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo, int32_t index); + + /** + * Remove a algorithm from configuration data. + * + * Removes the specified algorithm from configuration data. If + * the algorithm was not configured previously the function does + * not modify the configuration data and returns the number of + * free configuration data slots. + * + * If an application removes all algorithms then ZRTP does not + * include any algorithm into the hello message and falls back + * to a predefined mandatory algorithm. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The name of the algorithm to remove. + * @return + * Number of free configuration slots. + */ + int32_t zrtp_removeAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); + + /** + * Returns the number of configured algorithms. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @return + * The number of configured algorithms (used configuration + * data slots) + */ + int32_t zrtp_getNumConfiguredAlgos(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType); + + /** + * Returns the identifier of the algorithm at index. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @param index + * The index in the list of the algorihm type + * @return + * A pointer to the algorithm name. If the index + * does not point to a configured slot then the function + * returns NULL. + * + */ + const char* zrtp_getAlgoAt(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, int32_t index); + + /** + * Checks if the configuration data of the algorihm type already contains + * a specific algorithms. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The name of the algorithm to check + * @return + * True if the algorithm was found, false otherwise. + * + */ + int32_t zrtp_containsAlgo(ZrtpContext* zrtpContext, Zrtp_AlgoTypes algoType, const char* algo); + + /** + * Enables or disables trusted MitM processing. + * + * For further details of trusted MitM processing refer to ZRTP + * specification, chapter 7.3 + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param yesNo + * If set to true then trusted MitM processing is enabled. + */ + void zrtp_setTrustedMitM(ZrtpContext* zrtpContext, int32_t yesNo); + + /** + * Check status of trusted MitM processing. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * Returns true if trusted MitM processing is enabled. + */ + int32_t zrtp_isTrustedMitM(ZrtpContext* zrtpContext); + + /** + * Enables or disables SAS signature processing. + * + * For further details of trusted MitM processing refer to ZRTP + * specification, chapter 7.2 + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @param yesNo + * If true then certificate processing is enabled. + */ + void zrtp_setSasSignature(ZrtpContext* zrtpContext, int32_t yesNo); + + /** + * Check status of SAS signature processing. + * + * @param zrtpContext + * Pointer to the opaque ZrtpContext structure. + * @return + * Returns true if certificate processing is enabled. + */ + int32_t zrtp_isSasSignature(ZrtpContext* zrtpContext); + +#ifdef __cplusplus +} +#pragma GCC visibility pop +#endif + +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/ZrtpCallback.h b/src/libzrtpcpp/ZrtpCallback.h new file mode 100644 index 0000000..957c4dd --- /dev/null +++ b/src/libzrtpcpp/ZrtpCallback.h @@ -0,0 +1,367 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPCALLBACK_H_ +#define _ZRTPCALLBACK_H_ + +/** + * @file ZrtpCallback.h + * @brief Callback interface between ZRTP and the RTP stack implementation + * @ingroup GNU_ZRTP + * @{ + */ + +#include <string> +#include <stdint.h> +#include <libzrtpcpp/ZrtpCodes.h> + +#ifndef __EXPORT + #if __GNUC__ >= 4 + #define __EXPORT __attribute__ ((visibility("default"))) + #define __LOCAL __attribute__ ((visibility("hidden"))) + #elif defined _WIN32 || defined __CYGWIN__ + #define __EXPORT __declspec(dllimport) + #define __LOCAL + #else + #define __EXPORT + #define __LOCAL + #endif +#endif + +/** + * This enum defines which role a ZRTP peer has. + * + * According to the ZRTP specification the role determines which keys to + * use to encrypt or decrypt SRTP data. + * + * <ul> + * <li> The Initiator encrypts SRTP data using the <em>keyInitiator</em> and the + * <em>saltInitiator</em> data, the Responder uses these data to decrypt. + * </li> + * <li> The Responder encrypts SRTP data using the <em>keyResponder</em> and the + * <em>saltResponder</em> data, the Initiator uses these data to decrypt. + * </li> + * </ul> + */ +typedef enum { + Responder = 1, ///< This client is in ZRTP Responder mode + Initiator ///< This client is in ZRTP Initiator mode +} Role; + +/// The algorihms that we support in SRTP and that ZRTP can negotiate. +typedef enum { + None, + Aes = 1, ///< Use AES as symmetrical cipher algorithm + TwoFish, ///< Use TwoFish as symmetrical cipher algorithm + Sha1, ///< Use Sha1 as authentication algorithm + Skein ///< Use Skein as authentication algorithm +} SrtpAlgorithms; + +/** + * This structure contains pointers to the SRTP secrets and the role info. + * + * About the role and what the meaning of the role is refer to the + * of the enum Role. The pointers to the secrets are valid as long as + * the ZRtp object is active. To use these data after the ZRtp object's + * lifetime you may copy the data into a save place. The destructor + * of ZRtp clears the data. + */ +typedef struct srtpSecrets { + SrtpAlgorithms symEncAlgorithm; ///< symmetrical cipher algorithm + const uint8_t* keyInitiator; ///< Initiator's key + int32_t initKeyLen; ///< Initiator's key length + const uint8_t* saltInitiator; ///< Initiator's salt + int32_t initSaltLen; ///< Initiator's salt length + const uint8_t* keyResponder; ///< Responder's key + int32_t respKeyLen; ///< Responder's key length + const uint8_t* saltResponder; ///< Responder's salt + int32_t respSaltLen; ///< Responder's salt length + SrtpAlgorithms authAlgorithm; ///< SRTP authentication algorithm + int32_t srtpAuthTagLen; ///< SRTP authentication length + std::string sas; ///< The SAS string + Role role; ///< ZRTP role of this client +} SrtpSecret_t; + +enum EnableSecurity { + ForReceiver = 1, ///< Enable security for SRTP receiver + ForSender = 2 ///< Enable security for SRTP sender +}; + +/** + * This abstract class defines the callback functions required by GNU ZRTP. + * + * This class is a pure abstract class, aka Interface in Java, that + * defines the callback interface that the specific part of a GNU ZRTP + * must implement. The generic part of GNU ZRTP uses these mehtods + * to communicate with the specific part, for example to send data + * via the RTP/SRTP stack, to set timers and cancel timer and so on. + * + * The generiy part of GNU ZRTP needs only a few callback methods to + * be implemented by the specific part. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpCallback { + +protected: + friend class ZRtp; + + virtual ~ZrtpCallback() {}; + + /** + * Send a ZRTP packet via RTP. + * + * ZRTP calls this method to send a ZRTP packet via the RTP session. + * + * @param data + * Points to ZRTP packet to send. + * @param length + * The length in bytes of the data + * @return + * zero if sending failed, one if packet was send + */ + virtual int32_t sendDataZRTP(const uint8_t* data, int32_t length) =0; + + /** + * Activate timer. + * + * @param time + * The time in ms for the timer + * @return + * zero if activation failed, one if timer was activated + */ + virtual int32_t activateTimer(int32_t time) =0; + + /** + * Cancel the active timer. + * + * @return + * zero if cancel action failed, one if timer was canceled + */ + virtual int32_t cancelTimer() =0; + + /** + * Send information messages to the hosting environment. + * + * The ZRTP implementation uses this method to send information + * messages to the host. Along with the message ZRTP provides a + * severity indicator that defines: Info, Warning, Error, + * Alert. Refer to the <code>MessageSeverity</code> enum above. + * + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + virtual void sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) =0; + + /** + * SRTP crypto data ready for the sender or receiver. + * + * The ZRTP implementation calls this method right after all SRTP + * secrets are computed and ready to be used. The parameter points + * to a structure that contains pointers to the SRTP secrets and a + * <code>enum Role</code>. The called method (the implementation + * of this abstract method) must either copy the pointers to the SRTP + * data or the SRTP data itself to a save place. The SrtpSecret_t + * structure is destroyed after the callback method returns to the + * ZRTP implementation. + * + * The SRTP data themselfs are ontained in the ZRtp object and are + * valid as long as the ZRtp object is active. TheZRtp's + * destructor clears the secrets. Thus the called method needs to + * save the pointers only, ZRtp takes care of the data. + * + * The implementing class may enable SRTP processing in this + * method or delay it to srtpSecertsOn(). + * + * @param secrets A pointer to a SrtpSecret_t structure that + * contains all necessary data. + * + * @param part for which part (Sender or Receiver) this data is + * valid. + * + * @return Returns false if something went wrong during + * initialization of SRTP context, for example memory shortage. + */ + virtual bool srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part) =0; + + /** + * Switch off the security for the defined part. + * + * @param part Defines for which part (sender or receiver) to + * switch on security + */ + virtual void srtpSecretsOff(EnableSecurity part) =0; + + /** + * Switch on the security. + * + * ZRTP calls this method after it has computed the SAS and check + * if it is verified or not. In addition ZRTP provides information + * about the cipher algorithm and key length for the SRTP session. + * + * This method must enable SRTP processing if it was not enabled + * during sertSecretsReady(). + * + * @param c The name of the used cipher algorithm and mode, or + * NULL + * + * @param s The SAS string + * + * @param verified if <code>verified</code> is true then SAS was + * verified by both parties during a previous call. + */ + virtual void srtpSecretsOn(std::string c, std::string s, bool verified) =0; + + /** + * This method handles GoClear requests. + * + * According to the ZRTP specification the user must be informed about + * a GoClear request because the ZRTP implementation switches off security + * if it could authenticate the GoClear packet. + * + * <b>Note:</b> GoClear is not yet implemented in GNU ZRTP. + * + */ + virtual void handleGoClear() =0; + + /** + * Handle ZRTP negotiation failed. + * + * ZRTP calls this method in case ZRTP negotiation failed. The + * parameters show the severity as well as the reason. + * + * @param severity + * This defines the message's severity + * @param subCode + * The subcode identifying the reason. + * @see ZrtpCodes#MessageSeverity + */ + virtual void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) =0; + + /** + * ZRTP calls this method if the other side does not support ZRTP. + * + * If the other side does not answer the ZRTP <em>Hello</em> packets then + * ZRTP calls this method, + * + */ + virtual void zrtpNotSuppOther() =0; + + /** + * Enter synchronization mutex. + * + * GNU ZRTP requires one mutes to synchronize its + * processing. Because mutex implementations depend on the + * underlying infrastructure, for example operating system or + * thread implementation, GNU ZRTP delegates mutex handling to the + * spcific part of its implementation. + */ + virtual void synchEnter() =0; + + /** + * Leave synchronization mutex. + */ + virtual void synchLeave() =0; + + /** + * Inform about a PBX enrollment request. + * + * Please refer to chapter 8.3 ff to get more details about PBX + * enrollment and SAS relay. + * + * <b>Note:</b> PBX enrollement is not yet fully supported by GNU + * ZRTP. + * + * @param info Give some information to the user about the PBX + * requesting an enrollment. + */ + virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) =0; + + /** + * Inform about PBX enrollment result. + * + * Informs the use about the acceptance or denial of an PBX enrollment + * request + * + * <b>Note:</b> PBX enrollement is not yet fully supported by GNU + * ZRTP. + * + * @param info information to the user about the result + * of an enrollment. + */ + virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) =0; + + /** + * Request a SAS signature. + * + * After ZRTP was able to compute the Short Authentication String + * (SAS) it calls this method. The client may now use an + * approriate method to sign the SAS. The client may use + * ZrtpQueue#setSignatureData() to store the signature data an + * enable signature transmission to the other peer. Refer to + * chapter 8.2 of ZRTP specification. + * + * <b>Note:</b> SAS signing is not yet fully supported by GNU + * ZRTP. + * + * @param sasHash + * The SAS hash to sign. + * + */ + virtual void signSAS(uint8_t* sasHash) =0; + + /** + * ZRTPQueue calls this method to request a SAS signature check. + * + * After ZRTP received a SAS signature in one of the Confirm packets it + * call this method. The client may use <code>getSignatureLength()</code> + * and <code>getSignatureData()</code>of ZrtpQueue to get the signature + * data and perform the signature check. Refer to chapter 8.2 of ZRTP + * specification. + * + * If the signature check fails the client may return false to ZRTP. In + * this case ZRTP signals an error to the other peer and terminates + * the ZRTP handshake. + * + * <b>Note:</b> SAS signing is not yet fully supported by GNU + * ZRTP. + * + * @param sasHash + * The SAS hash that was signed by the other peer. + * @return + * true if the signature was ok, false otherwise. + * + */ + virtual bool checkSASSignature(uint8_t* sasHash) =0; +}; + +#endif // ZRTPCALLBACK + +/** + * @} + */ +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/ZrtpCallbackWrapper.h b/src/libzrtpcpp/ZrtpCallbackWrapper.h new file mode 100644 index 0000000..dd739e0 --- /dev/null +++ b/src/libzrtpcpp/ZrtpCallbackWrapper.h @@ -0,0 +1,101 @@ +/* + This class maps the ZRTP C++ callback methods to C callback methods. + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef ZRTPCALLBACKWRAPPER_H +#define ZRTPCALLBACKWRAPPER_H + +#include <stdlib.h> + +#include <libzrtpcpp/ZrtpCallback.h> +#include <libzrtpcpp/ZrtpConfigure.h> +#include <libzrtpcpp/ZrtpCWrapper.h> + +/** + * + * @file ZrtpCallbackWrapper.h + * @brief C-Wrapper helper + * + * This is a helper class for for the C wrapper and implements + * the GNU ZRTP callback interface. For detailed documentation about + * the callback method refer to file <code>ZrtpCallback</code> + * @ingroup GNU_ZRTP + * @{ + * + * @see ZrtpCallback + * @see ZrtpCWrapper + */ +class __EXPORT ZrtpCallbackWrapper : public ZrtpCallback +{ +public: + /** + * Construct a class that implements ZrtpCallback and uses a C structure + * to call C functions that implement the callbacks. + * + * @param cb + * The C callback structure that hold the addresses of the C methods + * that implement the actual callback functions. + * @param ctx + * Pointer to the ZrtpContext + */ + ZrtpCallbackWrapper(zrtp_Callbacks* cb, ZrtpContext* ctx); + + int32_t sendDataZRTP ( const unsigned char* data, int32_t length ); + + int32_t activateTimer ( int32_t time ); + + int32_t cancelTimer(); + + void sendInfo ( GnuZrtpCodes::MessageSeverity severity, int32_t subCode ); + + bool srtpSecretsReady ( SrtpSecret_t* secrets, EnableSecurity part ); + + void srtpSecretsOff ( EnableSecurity part ); + + void srtpSecretsOn ( std::string c, std::string s, bool verified ); + + void handleGoClear(); + + void zrtpNegotiationFailed ( GnuZrtpCodes::MessageSeverity severity, int32_t subCode ); + + void zrtpNotSuppOther(); + + void synchEnter(); + + void synchLeave(); + + void zrtpAskEnrollment (GnuZrtpCodes::InfoEnrollment info ); + + void zrtpInformEnrollment (GnuZrtpCodes::InfoEnrollment info ); + + void signSAS (uint8_t* sasHash ); + + bool checkSASSignature (uint8_t* sasHash ); + +private: + void init(); + zrtp_Callbacks *c_callbacks; + ZrtpContext* zrtpCtx; + +}; + +/** + * @} + */ + +#endif // ZRTPCALLBACKWRAPPER_H diff --git a/src/libzrtpcpp/ZrtpCodes.h b/src/libzrtpcpp/ZrtpCodes.h new file mode 100755 index 0000000..a1bb6fc --- /dev/null +++ b/src/libzrtpcpp/ZrtpCodes.h @@ -0,0 +1,164 @@ +/** @file ZrtpCodes.h + */ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPCODES_H_ +#define _ZRTPCODES_H_ +/** + * @file ZrtpCodes.h + * @brief The ZRTP info, warning, and error codes + * @ingroup GNU_ZRTP + * @{ + */ + +namespace GnuZrtpCodes { +/** + * \namespace GnuZrtpCodes + * + * This enum defines the information message severity. + * + * The ZRTP implementation issues information messages to inform the user + * about ongoing processing, unusual behavior, or alerts in case of severe + * problems. Each main severity code a number of sub-codes exist that + * specify the exact nature of the problem. + * + * An application gets message severity codes and the associated sub-codes + * via the ZrtpUserCallback#showMessage method. + * + * The severity levels and their meaning are: + * + * <dl> + * <dt>Info</dt> <dd>keeps the user informed about ongoing processing and + * security setup. The enumeration InfoCodes defines the subcodes. + * </dd> + * <dt>Warning</dt> <dd>is an information about some security issues, e.g. if + * an AES 256 encryption is request but only DH 3072 as public key scheme + * is supported. ZRTP will establish a secure session (SRTP). The + * enumeration WarningCodes defines the sub-codes. + * </dd> + * <dt>Severe</dt> <dd>is used if an error occured during ZRTP protocol usage. + * In case of <em>Severe</em> ZRTP will <b>not</b> establish a secure session. + * The enumeration SevereCodes defines the sub-codes. + * </dd> + * <dt>Zrtp</dt> <dd>shows a ZRTP security problem. Refer to the enumeration + * ZrtpErrorCodes for sub-codes. GNU ZRTP of course will <b>not</b> + * establish a secure session. + * </dd> + * </dl> + * + */ +enum MessageSeverity { + Info = 1, + Warning, + Severe, + ZrtpError +}; + +/** + * Sub-codes for Info + */ +enum InfoCodes { + InfoHelloReceived = 1, //!< Hello received, preparing a Commit + InfoCommitDHGenerated, //!< Commit: Generated a public DH key + InfoRespCommitReceived, //!< Responder: Commit received, preparing DHPart1 + InfoDH1DHGenerated, //!< DH1Part: Generated a public DH key + InfoInitDH1Received, //!< Initiator: DHPart1 received, preparing DHPart2 + InfoRespDH2Received, //!< Responder: DHPart2 received, preparing Confirm1 + InfoInitConf1Received, //!< Initiator: Confirm1 received, preparing Confirm2 + InfoRespConf2Received, //!< Responder: Confirm2 received, preparing Conf2Ack + InfoRSMatchFound, //!< At least one retained secrets matches - security OK + InfoSecureStateOn, //!< Entered secure state + InfoSecureStateOff //!< No more security for this session +}; + +/** + * Sub-codes for Warning + */ +enum WarningCodes { + WarningDHAESmismatch = 1, //!< Commit contains an AES256 cipher but does not offer a Diffie-Helman 4096 + WarningGoClearReceived, //!< Received a GoClear message + WarningDHShort, //!< Hello offers an AES256 cipher but does not offer a Diffie-Helman 4096 + WarningNoRSMatch, //!< No retained shared secrets available - must verify SAS + WarningCRCmismatch, //!< Internal ZRTP packet checksum mismatch - packet dropped + WarningSRTPauthError, //!< Dropping packet because SRTP authentication failed! + WarningSRTPreplayError, //!< Dropping packet because SRTP replay check failed! + WarningNoExpectedRSMatch //!< Valid retained shared secrets availabe but no matches found - must verify SAS +}; + +/** + * Sub-codes for Severe + */ +enum SevereCodes { + SevereHelloHMACFailed = 1, //!< Hash HMAC check of Hello failed! + SevereCommitHMACFailed, //!< Hash HMAC check of Commit failed! + SevereDH1HMACFailed, //!< Hash HMAC check of DHPart1 failed! + SevereDH2HMACFailed, //!< Hash HMAC check of DHPart2 failed! + SevereCannotSend, //!< Cannot send data - connection or peer down? + SevereProtocolError, //!< Internal protocol error occured! + SevereNoTimer, //!< Cannot start a timer - internal resources exhausted? + SevereTooMuchRetries //!< Too much retries during ZRTP negotiation - connection or peer down? +}; + +/** + * Error codes according to the ZRTP specification chapter 6.9 + * + * GNU ZRTP uses these error codes in two ways: to fill the appropriate + * field ing the ZRTP Error packet and as sub-code in + * ZrtpUserCallback#showMessage(). GNU ZRTP uses thes error codes also + * to report received Error packts, in this case the sub-codes are their + * negative values. + * + * The enumeration member comments are copied from the ZRTP specification. + */ +enum ZrtpErrorCodes { + MalformedPacket = 0x10, //!< Malformed packet (CRC OK, but wrong structure) + CriticalSWError = 0x20, //!< Critical software error + UnsuppZRTPVersion = 0x30, //!< Unsupported ZRTP version + HelloCompMismatch = 0x40, //!< Hello components mismatch + UnsuppHashType = 0x51, //!< Hash type not supported + UnsuppCiphertype = 0x52, //!< Cipher type not supported + UnsuppPKExchange = 0x53, //!< Public key exchange not supported + UnsuppSRTPAuthTag = 0x54, //!< SRTP auth. tag not supported + UnsuppSASScheme = 0x55, //!< SAS scheme not supported + NoSharedSecret = 0x56, //!< No shared secret available, DH mode required + DHErrorWrongPV = 0x61, //!< DH Error: bad pvi or pvr ( == 1, 0, or p-1) + DHErrorWrongHVI = 0x62, //!< DH Error: hvi != hashed data + SASuntrustedMiTM = 0x63, //!< Received relayed SAS from untrusted MiTM + ConfirmHMACWrong = 0x70, //!< Auth. Error: Bad Confirm pkt HMAC + NonceReused = 0x80, //!< Nonce reuse + EqualZIDHello = 0x90, //!< Equal ZIDs in Hello + GoCleatNotAllowed = 0x100, //!< GoClear packet received, but not allowed + IgnorePacket = 0x7fffffff +}; + +/** + * Information codes for the Enrollment user callbacks. + */ +enum InfoEnrollment { + EnrollmentRequest, //!< Aks user to confirm or deny an Enrollemnt request + EnrollmentCanceled, //!< User did not confirm the PBX enrollement + EnrollmentFailed, //!< Enrollment process failed, no PBX secret available + EnrollmentOk //!< Enrollment process for this PBX was ok +}; + +} + +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/ZrtpConfigure.h b/src/libzrtpcpp/ZrtpConfigure.h new file mode 100644 index 0000000..33a824f --- /dev/null +++ b/src/libzrtpcpp/ZrtpConfigure.h @@ -0,0 +1,551 @@ +/* + Copyright (C) 2009 - 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _ZRTPCONFIGURE_H_ +#define _ZRTPCONFIGURE_H_ + +/** + * @file ZrtpConfigure.h + * @brief The ZRTP configure functions + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdio.h> +#include <stdint.h> +#include <list> +#include <string> +#include <vector> +#include <string.h> + +#include <libzrtpcpp/ZrtpCallback.h> + +/** + * This enumerations list all configurable algorithm types. + */ + +enum AlgoTypes { + Invalid = 0, HashAlgorithm = 1, CipherAlgorithm, PubKeyAlgorithm, SasType, AuthLength +}; + +typedef void(*encrypt_t)(uint8_t*, int32_t, uint8_t*, uint8_t*, int32_t); +typedef void(*decrypt_t)(uint8_t*, int32_t, const uint8_t*, uint8_t*, int32_t); + +/** + * The algorithm enumration class. + * + * This simple class is just a container of an algorithm's name and + * its associated algorithm type. We use this class together with the + * EnumBase class to implement a Java-like enum class functionality + * (not fully, but OK for our use case). + * + * An application shall use the get / check methods to retrieve information. + */ +class AlgorithmEnum { +public: + /** + * Create an AlgorithmEnum object. + * + * @param type + * Defines the algorithm type + * @param name + * Set the names of the algorithm. The name is copied + * and the call may reuse the space. + * @param klen + * The key length for this algorihm in byte, for example 16 or 32 + * @param ra + * A human readable short string that describes the algorihm. + * @param en + * Pointer to the encryption function of this algorithn + * @param de + * Pointer to the decryption funtions of this algorithm. + * @param alId + * The algorithm id used by SRTP to identify an algorithm type, for + * example Skein, Sha1, Aes, ... + * + * @see AlgoTypes + */ + AlgorithmEnum(const AlgoTypes type, const char* name, int32_t klen, + const char* ra, encrypt_t en, decrypt_t de, SrtpAlgorithms alId); + + /** + * AlgorithmEnum destructor + */ + ~AlgorithmEnum(); + + /** + * Get the algorihm's name + * + * @returns + * Algorithm's name as null terminated C-string. The + * application must not free this memory. + */ + const char* getName(); + + /** + * Get the algorihm's readable name + * + * @returns + * Algorithm's readable name as null terminated C-string. The + * application must not free this memory. + */ + const char* getReadable(); + + /** + * Get the algorihm's key length. + * + * @returns + * An integer definig the key length in bytes. + */ + int getKeylen(); + + /** + * Get the algorihm's integer id. + * + * @returns + * An integer that defines the algorithm. + */ + SrtpAlgorithms getAlgoId(); + /** + * Get the algorihm's key length. + * + * @returns + * An integer definig the key length in bytes. + */ + encrypt_t getEncrypt(); + + /** + * Get the algorihm's key length. + * + * @returns + * An integer definig the key length in bytes. + */ + decrypt_t getDecrypt(); + + /** + * Get the algorithm type of this AlgorithmEnum object. + * + * @returns + * The algorithm type. + * + * @see AlgoTypes + */ + AlgoTypes getAlgoType(); + + /** + * Check if this AlgorithmEnum object is valid + * + * @returns + * @c true if the object is valid, @c false otherwise + */ + bool isValid(); + +private: + AlgoTypes algoType; + std::string algoName; + int32_t keyLen; + std::string readable; + encrypt_t encrypt; + decrypt_t decrypt; + SrtpAlgorithms algoId; +}; + +/** + * EnumBase provides methods to store and access algorithm enumerations of + * a specific algorithm type. + * + * An application shall use the get / check methods to retrieve information + * from the preset Algorithm Enumerations. + * + * @see AlgoTypes + * @see zrtpHashes + * @see zrtpSymCiphers + * @see zrtpPubKeys + * @see zrtpSasTypes + * @see zrtpAuthLengths + */ +class EnumBase { +public: + /** + * Get an AlgorithmEnum by its name + * + * @param name + * The name of the AlgorithmEnum to search. + * @returns + * The AlgorithmEnum if found or an invalid AlgorithmEnum if the name + * was not found + */ + AlgorithmEnum& getByName(const char* name); + + /** + * Return all names of all currently stored AlgorithmEnums + * + * @return + * A C++ std::list of C++ std::strings that contain the names. + */ + std::list<std::string>* getAllNames(); + + /** + * Get the number of currently stored AlgorithmEnums + * + * @return + * The number of currently stored AlgorithmEnums + */ + int getSize(); + + /** + * Get the AlgoTypes to which this EnumBase belongs. + * + * @return + * The AlgoTypes of this EnumBase. + * @see AlgoTypes. + */ + AlgoTypes getAlgoType(); + + /** + * Return the AlgorithmEnum by its ordinal number + * + * @param ord + * The ordinal number of the AlgorithmEnum. + * @return + * The AlgorithmEnum if found, an invalid Algorithm otherwise. + */ + AlgorithmEnum& getByOrdinal(int ord); + + /** + * Get the ordinal number of an AlgorithmEnum + * + * @param algo + * Return toe ordinal numer of this AlgorithmEnum. + * + * @return + * Return the ordinal number of this AlgorithmEnum if found, + * -1 otherwise. + */ + int getOrdinal(AlgorithmEnum& algo); + +protected: + EnumBase(AlgoTypes algo); + ~EnumBase(); + void insert(const char* name); + void insert(const char* name, int32_t klen, + const char* ra, encrypt_t en, decrypt_t de, SrtpAlgorithms alId); + +private: + AlgoTypes algoType; + std::vector <AlgorithmEnum* > algos; +}; + +/** + * The enumaration subclasses that contain the supported algorithm enumerations. + */ +class HashEnum : public EnumBase { +public: + HashEnum(); + ~HashEnum(); +}; + +class SymCipherEnum : public EnumBase { +public: + SymCipherEnum(); + ~SymCipherEnum(); +}; + +class PubKeyEnum : public EnumBase { +public: + PubKeyEnum(); + ~PubKeyEnum(); +}; + +class SasTypeEnum : public EnumBase { +public: + SasTypeEnum(); + ~SasTypeEnum(); +}; + +class AuthLengthEnum : public EnumBase { +public: + AuthLengthEnum(); + ~AuthLengthEnum(); +}; + +extern HashEnum zrtpHashes; +extern SymCipherEnum zrtpSymCiphers; +extern PubKeyEnum zrtpPubKeys; +extern SasTypeEnum zrtpSasTypes; +extern AuthLengthEnum zrtpAuthLengths; + +/** + * ZRTP configuration data. + * + * This class contains data and functions to set ZRTP configuration data. + * An application may use this class to set configuration information for + * ZRTP. ZRTP uses this configuration information to announce various + * algorithms via its Hello message. An application may use this class to + * restrict or allow use of algorithms. + * + * The constructor does not set any algorithms, thus it is an empty + * configuration. An application may use this empty configuration and + * hand it over to ZRTP. In this case ZRTP does not announce any algorithms + * in its Hello message and uses mandatory algorithms only. + * + * An application can configure implemented algorithms only. + */ +class __EXPORT ZrtpConfigure { +public: + ZrtpConfigure(); /* Creates Configuration data */ + ~ZrtpConfigure(); + + /** + * Set the maximum number of algorithms per algorithm type that an application can + * configure. + */ + static const int maxNoOfAlgos = 7; + /** + * Convenience function that sets a pre-defined standard configuration. + * + * The standard configuration consists of the following algorithms: + * <ul> + * <li> Hash: SHA256 </li> + * <li> Symmetric Cipher: AES 128, AES 256 </li> + * <li> Public Key Algorithm: DH2048, DH3027, MultiStream </li> + * <li> SAS type: libase 32 </li> + * <li> SRTP Authentication lengths: 32, 80 </li> + *</ul> + */ + void setStandardConfig(); + + /** + * Convenience function that sets the mandatory algorithms only. + * + * Mandatory algorithms are: + * <ul> + * <li> Hash: SHA256 </li> + * <li> Symmetric Cipher: AES 128 </li> + * <li> Public Key Algorithm: DH3027, MultiStream </li> + * <li> SAS type: libase 32 </li> + * <li> SRTP Authentication lengths: 32, 80 </li> + *</ul> + */ + void setMandatoryOnly(); + + /** + * Clear all configuration data. + * + * The functions clears all configuration data. + */ + void clear(); + + /** + * Add an algorithm to configuration data. + * + * Adds the specified algorithm to the configuration data. + * If no free configuration data slot is available the + * function does not add the algorithm and returns -1. The + * methods appends the algorithm to the existing algorithms. + * + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The enumeration of the algorithm to add. + * @return + * Number of free configuration data slots or -1 on error + */ + int32_t addAlgo(AlgoTypes algoType, AlgorithmEnum& algo); + + /** + * Add an algorithm to configuration data at given index. + * + * Adds the specified algorithm to the configuration data vector + * at a given index. If the index is larger than the actual size + * of the configuration vector the method just appends the algorithm. + * + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The enumeration of the algorithm to add. + * @param index + * The index where to add the algorihm + * @return + * Number of free configuration data slots or -1 on error + */ + int32_t addAlgoAt(AlgoTypes algoType, AlgorithmEnum& algo, int32_t index); + + /** + * Remove a algorithm from configuration data. + * + * Removes the specified algorithm from configuration data. If + * the algorithm was not configured previously the function does + * not modify the configuration data and returns the number of + * free configuration data slots. + * + * If an application removes all algorithms then ZRTP does not + * include any algorithm into the hello message and falls back + * to a predefined mandatory algorithm. + * + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The enumeration of the algorithm to remove. + * @return + * Number of free configuration slots. + */ + int32_t removeAlgo(AlgoTypes algoType, AlgorithmEnum& algo); + + /** + * Returns the number of configured algorithms. + * + * @param algoType + * Specifies which algorithm type to select + * @return + * The number of configured algorithms (used configuration + * data slots) + */ + int32_t getNumConfiguredAlgos(AlgoTypes algoType); + + /** + * Returns the identifier of the algorithm at index. + * + * @param algoType + * Specifies which algorithm type to select + * @param index + * The index in the list of the algorihm type + * @return + * A pointer the the algorithm enumeration. If the index + * does not point to a configured slot then the function + * returns NULL. + * + */ + AlgorithmEnum& getAlgoAt(AlgoTypes algoType, int32_t index); + + /** + * Checks if the configuration data of the algorihm type already contains + * a specific algorithms. + * + * @param algoType + * Specifies which algorithm type to select + * @param algo + * The algorithm to check + * @return + * True if the algorithm was found, false otherwise. + * + */ + bool containsAlgo(AlgoTypes algoType, AlgorithmEnum& algo); + + /** + * Enables or disables trusted MitM processing. + * + * For further details of trusted MitM processing refer to ZRTP + * specification, chapter 7.3 + * + * @param yesNo + * If set to true then trusted MitM processing is enabled. + */ + void setTrustedMitM(bool yesNo); + + /** + * Check status of trusted MitM processing. + * + * @return + * Returns true if trusted MitM processing is enabled. + */ + bool isTrustedMitM(); + + /** + * Enables or disables SAS signature processing. + * + * For further details of trusted MitM processing refer to ZRTP + * specification, chapter 7.2 + * + * @param yesNo + * If set to true then certificate processing is enabled. + */ + void setSasSignature(bool yesNo); + + /** + * Check status of SAS signature processing. + * + * @return + * Returns true if certificate processing is enabled. + */ + bool isSasSignature(); + + /** + * Enables or disables paranoid mode. + * + * For further explanation of paranoid mode refer to the documentation + * of ZRtp class. + * + * @param yesNo + * If set to true then paranoid mode is enabled. + */ + void setParanoidMode(bool yesNo); + + /** + * Check status of paranoid mode. + * + * @return + * Returns true if paranoid mode is enabled. + */ + bool isParanoidMode(); + + /// Helper function to print some internal data + void printConfiguredAlgos(AlgoTypes algoTyp); + + private: + std::vector<AlgorithmEnum* > hashes; + std::vector<AlgorithmEnum* > symCiphers; + std::vector<AlgorithmEnum* > publicKeyAlgos; + std::vector<AlgorithmEnum* > sasTypes; + std::vector<AlgorithmEnum* > authLengths; + + bool enableTrustedMitM; + bool enableSasSignature; + bool enableParanoidMode; + + + AlgorithmEnum& getAlgoAt(std::vector<AlgorithmEnum* >& a, int32_t index); + int32_t addAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo); + int32_t addAlgoAt(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo, int32_t index); + int32_t removeAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo); + int32_t getNumConfiguredAlgos(std::vector<AlgorithmEnum* >& a); + bool containsAlgo(std::vector<AlgorithmEnum* >& a, AlgorithmEnum& algo); + std::vector<AlgorithmEnum* >& getEnum(AlgoTypes algoType); + + void printConfiguredAlgos(std::vector<AlgorithmEnum* >& a); + + protected: + + public: +}; + +/** + * @} + */ +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/ZrtpCrc32.h b/src/libzrtpcpp/ZrtpCrc32.h new file mode 100755 index 0000000..ad57edd --- /dev/null +++ b/src/libzrtpcpp/ZrtpCrc32.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPCRC32_H_ +#define _ZRTPCRC32_H_ + +/** + * + * @file ZrtpCrc32.h + * @brief Methods to compute the CRC32 checksum for ZRTP packets + * + * @ingroup GNU_ZRTP + * @{ + * + * @see ZrtpCallback + */ + +/** + * Check if a buffer matches a given CRC32 checksum. + * + * @param buffer + * Pointer to the data buffer. + * @param length + * Length in bytes of the data buffer. + * @param crc32 + * The CRC32 checksum. + * + * @return + * @c true if the CRC32 checksum matches the computed checksum of the + * buffer, @c false otherwise. + */ +bool zrtpCheckCksum(uint8_t *buffer, uint16_t length, uint32_t crc32); + +/** + * Generate a CRC32 checksum of a data buffer + * + * @param buffer + * Pointer to the buffer. + * @param length + * Lenght of the buffer in bytes. + * + * @return + * A preliminary CRC32 checksum + */ +uint32_t zrtpGenerateCksum(uint8_t *buffer, uint16_t length); + +/** + * Close CRC32 computation. + * + * @param crc32 + * A preliminary CRC32 checksum. + * + * @return + * The ready to use CRC32 checksum in host order. + */ +uint32_t zrtpEndCksum(uint32_t crc32); + +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/ZrtpPacketBase.h b/src/libzrtpcpp/ZrtpPacketBase.h new file mode 100644 index 0000000..f0d2944 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketBase.h @@ -0,0 +1,147 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _ZRTPPACKETBASE_H_ +#define _ZRTPPACKETBASE_H_ + +/** + * @file ZrtpPacketBase.h + * @brief The ZRTP message header class + * + * This class defines the ZRTP message header and provides access and + * check methods. + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) +#include <winsock2.h> +#else +#include <netinet/in.h> +#endif + +#include <libzrtpcpp/zrtpPacket.h> +#include <libzrtpcpp/ZrtpTextData.h> +#include <libzrtpcpp/ZrtpConfigure.h> +#include <libzrtpcpp/ZrtpCrc32.h> + +// #define DEBUGOUT(deb) deb +#define DEBUGOUT(deb) + +/* + * This is the unique ZRTP ID in network order (PZ) + */ +const uint16_t zrtpId = 0x505a; + +/** + * This is the base class for all ZRTP packets + * + * All other ZRTP packet classes inherit from this class. It does not have + * an implementation of its own. + * + * The standard constructors of the subclasses usually initialize the @c allocate + * field with their fixed data array which is large enough to hold all message + * data. If an implementation needs to change this to use dynamic memory + * allocation only that line in the subclasses must be changed and the destructors + * should take care of memory management. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketBase { + + private: + + protected: + void* allocated; ///< Pointer to ZRTP message data + zrtpPacketHeader_t* zrtpHeader; ///< points to the fixed ZRTP header structure + + public: + /** + * Destructor is empty + */ + virtual ~ZrtpPacketBase() {}; + + /** + * Get pointer to ZRTP header + * + * @return + * Pointer to ZRTP header structure. + */ + const uint8_t* getHeaderBase() { return (const uint8_t*)zrtpHeader; }; + + /** + * Check is this is a ZRTP message + * + * @return + * @c true if check was ok + */ + bool isZrtpPacket() { return (ntohs(zrtpHeader->zrtpId) == zrtpId); }; + + /** + * Get the length in words of the ZRTP message + * + * @return + * The length in words + */ + uint16_t getLength() { return ntohs(zrtpHeader->length); }; + + /** + * Return pointer to fixed length message type ASCII data + * + * @return + * Pointer to ASCII character array + */ + uint8_t* getMessageType() { return zrtpHeader->messageType; }; + + /** + * Set the lenght field in the ZRTP header + * + * @param len + * The length of the ZRTP message in words, host order + */ + void setLength(uint16_t len) { zrtpHeader->length = htons(len); }; + + /** + * Copy the message type ASCII data to ZRTP message type field + * + * @param msg + * Pointer to message type ASCII character array + */ + void setMessageType(uint8_t *msg) + { memcpy(zrtpHeader->messageType, msg, sizeof(zrtpHeader->messageType)); }; + + /** + * Initializes the ZRTP Id field + */ + void setZrtpId() { zrtpHeader->zrtpId = htons(zrtpId); } +}; + +/** + * @} + */ +#endif // ZRTPPACKETBASE diff --git a/src/libzrtpcpp/ZrtpPacketClearAck.h b/src/libzrtpcpp/ZrtpPacketClearAck.h new file mode 100644 index 0000000..992f6d8 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketClearAck.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETCLEARACK_H_ +#define _ZRTPPACKETCLEARACK_H_ + +/** + * @file ZrtpPacketClearAck.h + * @brief The ZRTP ClearAck message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the ClearAck packet - Currently not used + * + * The ZRTP simple message ClearAck. The implementation sends this + * after switching to clear mode (non-SRTP mode). + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZrtpPacketClearAck : public ZrtpPacketBase { + + public: + ZrtpPacketClearAck(); /// Creates a ClearAck packet with default data + ZrtpPacketClearAck(uint8_t* data); /// Creates a ClearAck packet from received data + virtual ~ZrtpPacketClearAck(); + + private: + ClearAckPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETCLEARACK + diff --git a/src/libzrtpcpp/ZrtpPacketCommit.h b/src/libzrtpcpp/ZrtpPacketCommit.h new file mode 100644 index 0000000..b23b23d --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketCommit.h @@ -0,0 +1,134 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ +#ifndef _ZRTPPACKETCOMMIT_H_ +#define _ZRTPPACKETCOMMIT_H_ + +/** + * @file ZrtpPacketCommit.h + * @brief The ZRTP Commit message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Commit packet. + * + * The ZRTP message Commit. The ZRTP implementation sends or receives + * this message to commit the crypto parameters offered during a Hello + * message. + * + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketCommit : public ZrtpPacketBase { + + protected: + Commit_t* commitHeader; ///< Points to Commit message part + + public: + /// Creates a Commit packet with default data + ZrtpPacketCommit(); + + /// Creates a Commit packet from received data + ZrtpPacketCommit(uint8_t* data); + + /// Normal destructor + virtual ~ZrtpPacketCommit(); + + /// Get pointer to hash algorithm type field, a fixed length character array + uint8_t* getHashType() { return commitHeader->hash; }; + + /// Get pointer to cipher algorithm type field, a fixed length character array + uint8_t* getCipherType() { return commitHeader->cipher; }; + + /// Get pointer to SRTP authentication algorithm type field, a fixed length character array + uint8_t* getAuthLen() { return commitHeader->authlengths; }; + + /// Get pointer to key agreement algorithm type field, a fixed length character array + uint8_t* getPubKeysType() { return commitHeader->pubkey; }; + + /// Get pointer to SAS algorithm type field, a fixed length character array + uint8_t* getSasType() { return commitHeader->sas; }; + + /// Get pointer to ZID field, a fixed length byte array + uint8_t* getZid() { return commitHeader->zid; }; + + /// Get pointer to HVI field, a fixed length byte array + uint8_t* getHvi() { return commitHeader->hvi; }; + + /// Get pointer to NONCE field, a fixed length byte array, overlaps HVI field + uint8_t* getNonce() { return commitHeader->hvi; }; + + /// Get pointer to hashH2 field, a fixed length byte array + uint8_t* getH2() { return commitHeader->hashH2; }; + + /// Get pointer to MAC field, a fixed length byte array + uint8_t* getHMAC() { return commitHeader->hmac; }; + + /// Get pointer to MAC field during multi-stream mode, a fixed length byte array + uint8_t* getHMACMulti() { return commitHeader->hmac-4*ZRTP_WORD_SIZE; }; + + /// Set hash algorithm type field, fixed length character field + void setHashType(uint8_t* text) { memcpy(commitHeader->hash, text, ZRTP_WORD_SIZE); }; + + /// Set cipher algorithm type field, fixed length character field + void setCipherType(uint8_t* text) { memcpy(commitHeader->cipher, text, ZRTP_WORD_SIZE); }; + + /// Set SRTP authentication algorithm algorithm type field, fixed length character field + void setAuthLen(uint8_t* text) { memcpy(commitHeader->authlengths, text, ZRTP_WORD_SIZE); }; + + /// Set key agreement algorithm type field, fixed length character field + void setPubKeyType(uint8_t* text) { memcpy(commitHeader->pubkey, text, ZRTP_WORD_SIZE); }; + + /// Set SAS algorithm type field, fixed length character field + void setSasType(uint8_t* text) { memcpy(commitHeader->sas, text, ZRTP_WORD_SIZE); }; + + /// Set ZID field, a fixed length byte array + void setZid(uint8_t* text) { memcpy(commitHeader->zid, text, sizeof(commitHeader->zid)); }; + + /// Set HVI field, a fixed length byte array + void setHvi(uint8_t* text) { memcpy(commitHeader->hvi, text, sizeof(commitHeader->hvi)); }; + + /// Set conce field, a fixed length byte array, overlapping HVI field + void setNonce(uint8_t* text); + + /// Set hashH2 field, a fixed length byte array + void setH2(uint8_t* hash) { memcpy(commitHeader->hashH2, hash, sizeof(commitHeader->hashH2)); }; + + /// Set MAC field, a fixed length byte array + void setHMAC(uint8_t* hash) { memcpy(commitHeader->hmac, hash, sizeof(commitHeader->hmac)); }; + + /// Set MAC field during multi-stream mode, a fixed length byte array + void setHMACMulti(uint8_t* hash) { memcpy(commitHeader->hmac-4*ZRTP_WORD_SIZE, hash, sizeof(commitHeader->hmac)); }; + + private: + CommitPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETCOMMIT + diff --git a/src/libzrtpcpp/ZrtpPacketConf2Ack.h b/src/libzrtpcpp/ZrtpPacketConf2Ack.h new file mode 100644 index 0000000..a7c2567 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketConf2Ack.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETCON2FACK_H_ +#define _ZRTPPACKETCON2FACK_H_ + +/** + * @file ZrtpPacketConf2Ack.h + * @brief The ZRTP Conf2Ack message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Conf2Ack packet. + * + * The ZRTP simple message Conf2Ack. The implementation sends this + * after receiving and checking the Confirm2 message. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketConf2Ack : public ZrtpPacketBase { + + public: + /// Creates a Conf2Ack packet with default data + ZrtpPacketConf2Ack(); + + ///Creates a Conf2Ack packet from received data + ZrtpPacketConf2Ack(char* data); + + /// Normal destructor + virtual ~ZrtpPacketConf2Ack(); + + private: + Conf2AckPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETCONF2ACK + diff --git a/src/libzrtpcpp/ZrtpPacketConfirm.h b/src/libzrtpcpp/ZrtpPacketConfirm.h new file mode 100644 index 0000000..b2dfbf4 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketConfirm.h @@ -0,0 +1,125 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETCONFIRM_H_ +#define _ZRTPPACKETCONFIRM_H_ + +/** + * @file ZrtpPacketConfirm.h + * @brief The ZRTP Confirm message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Confirm packet. + * + * The ZRTP message Confirm. The implementation sends this + * to confirm the switch to SRTP (encrypted) mode. The contents of + * the Confirm message are encrypted, thus the implementation + * can check if the secret keys work. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketConfirm : public ZrtpPacketBase { + + private: + Confirm_t* confirmHeader; ///< Point to the Confirm message part + + public: + /// Creates a Confirm packet with default data + ZrtpPacketConfirm(); + + /// Creates a Confirm packet with default data and a given signature length + ZrtpPacketConfirm(uint32_t sl); + + /// Creates a Confirm packet from received data + ZrtpPacketConfirm(uint8_t* d); + + /// Normal destructor + virtual ~ZrtpPacketConfirm(); + + /// Check if SAS verify flag is set + const bool isSASFlag() { return confirmHeader->flags & 0x4; } + + /// Check if PBXEnrollment flag is set + const bool isPBXEnrollment() { return confirmHeader->flags & 0x8; } + + /// Get pointer to filler bytes (contains one bit of signature length) + const uint8_t* getFiller() { return confirmHeader->filler; } + + /// Get pointer to IV data, fixed byte array + const uint8_t* getIv() { return confirmHeader->iv; } + + /// Get pointer to MAC data, fixed byte array + const uint8_t* getHmac() { return confirmHeader->hmac; } + + /// Get Expiration time data + const uint32_t getExpTime() { return ntohl(confirmHeader->expTime); } + + /// Get pointer to initial hash chain (H0) data, fixed byte array + uint8_t* getHashH0() { return confirmHeader->hashH0; } + + /// Get pointer to signature data, variable length, refer to getSignatureLength() + const uint8_t* getSignatureData() { return ((uint8_t*)&confirmHeader->expTime) + 4; } + + /// get the signature length in words + int32_t getSignatureLength(); + + /// set SAS verified flag + void setSASFlag() { confirmHeader->flags |= 0x4; } + + /// set setPBXEnrollment flag + void setPBXEnrollment() { confirmHeader->flags |= 0x8; } + + /// Set MAC data, fixed length byte array + void setHmac(uint8_t* text) { memcpy(confirmHeader->hmac, text, sizeof(confirmHeader->hmac)); } + + /// Set IV data, fixed length byte array + void setIv(uint8_t* text) { memcpy(confirmHeader->iv, text, sizeof(confirmHeader->iv)); } + + /// Set expiration time data + void setExpTime(uint32_t t) { confirmHeader->expTime = htonl(t); } + + /// Set initial hash chain (H0) data, fixed length byte array + void setHashH0(uint8_t* t) { memcpy(confirmHeader->hashH0, t, sizeof(confirmHeader->hashH0)); } + + /// Set signature data, length of the signature data in bytes and must be a multiple of 4. + bool setSignatureData(uint8_t* data, int32_t length); + + /// Set signature length in words + bool setSignatureLength(uint32_t sl); + + private: + void initialize(); + // Confirm packet is of variable length. It maximum size is 524 words: + // - 11 words fixed size + // - up to 513 words variable part, depending if signature is present and its length. + // This leads to a maximum of 4*524=2096 bytes. + uint8_t data[2100]; // large enough to hold a full blown Confirm packet + +}; + +/** + * @} + */ +#endif // ZRTPPACKETCONFIRM + diff --git a/src/libzrtpcpp/ZrtpPacketDHPart.h b/src/libzrtpcpp/ZrtpPacketDHPart.h new file mode 100644 index 0000000..d0ea4ba --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketDHPart.h @@ -0,0 +1,120 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETDHPART_H_ +#define _ZRTPPACKETDHPART_H_ + +/** + * @file ZrtpPacketDHPart.h + * @brief The ZRTP DHPart message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the DHPart packet. + * + * The ZRTP message DHPart. The implementation sends this + * to exchange the Diffie-Helman public keys and the shared + * secrets between the two parties. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketDHPart : public ZrtpPacketBase { + + protected: + uint8_t *pv; ///< points to public key value inside DH message + DHPart_t* DHPartHeader; ///< points to DH message structure + int32_t dhLength; ///< length of DH message, DH message has variable length + + public: + /// Creates a DHPart packet no data, must use setPubKeyType(...) + ZrtpPacketDHPart(); + + /// Creates a DHPart packet with default data and a give public key type + ZrtpPacketDHPart(const char* pkt); + + /// Creates a DHPart packet from received data + ZrtpPacketDHPart(uint8_t* data); + + /// Standard destructor + virtual ~ZrtpPacketDHPart(); + + /// Get pointer to public key value, variable length byte array + uint8_t* getPv() { return pv; } + + /// Get pointer to first retained secretd id, fixed length byte array + uint8_t* getRs1Id() { return DHPartHeader->rs1Id; }; + + /// Get pointer to second retained secretd id, fixed length byte array + uint8_t* getRs2Id() { return DHPartHeader->rs2Id; }; + + /// Get pointer to additional retained secretd id, fixed length byte array + uint8_t* getAuxSecretId() { return DHPartHeader->auxSecretId; }; + + /// Get pointer to PBX retained secretd id, fixed length byte array + uint8_t* getPbxSecretId() { return DHPartHeader->pbxSecretId; }; + + /// Get pointer to first hash (H1) for hash chain, fixed length byte array + uint8_t* getH1() { return DHPartHeader->hashH1; }; + + /// Get pointer to HMAC, fixed length byte array + uint8_t* getHMAC() { return pv+dhLength; }; + + /// Setpublic key value, variable length byte array + void setPv(uint8_t* text) { memcpy(pv, text, dhLength); }; + + /// Set first retained secretd id, fixed length byte array + void setRs1Id(uint8_t* text) { memcpy(DHPartHeader->rs1Id, text, sizeof(DHPartHeader->rs1Id)); }; + + /// Set second retained secretd id, fixed length byte array + void setRs2Id(uint8_t* text) { memcpy(DHPartHeader->rs2Id, text, sizeof(DHPartHeader->rs2Id)); }; + + /// Set additional retained secretd id, fixed length byte array + void setAuxSecretId(uint8_t* t) { memcpy(DHPartHeader->auxSecretId, t, sizeof(DHPartHeader->auxSecretId)); }; + + /// Set PBX retained secretd id, fixed length byte array + void setPbxSecretId(uint8_t* t) { memcpy(DHPartHeader->pbxSecretId,t, sizeof(DHPartHeader->pbxSecretId)); }; + + /// Set first hash (H1) of hash chain, fixed length byte array + void setH1(uint8_t* t) { memcpy(DHPartHeader->hashH1, t, sizeof(DHPartHeader->hashH1)); }; + + /// Set key agreement type, fixed size character array + void setPubKeyType(const char* pkt); + + /// Set first MAC, fixed length byte array + void setHMAC(uint8_t* t) { memcpy(pv+dhLength, t, 2*ZRTP_WORD_SIZE); }; + + private: + void initialize(); + // SupportedPubKeys pktype; + // DHPart packet is of variable length. It maximum size is 141 words: + // - 13 words fixed sizze + // - up to 128 words variable part, depending on DH algorithm + // leads to a maximum of 4*141=564 bytes. + uint8_t data[768]; // large enough to hold a full blown DHPart packet +}; + +/** + * @} + */ +#endif // ZRTPPACKETDHPART + diff --git a/src/libzrtpcpp/ZrtpPacketError.h b/src/libzrtpcpp/ZrtpPacketError.h new file mode 100644 index 0000000..d775801 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketError.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETERROR_H_ +#define _ZRTPPACKETERROR_H_ + +/** + * @file ZrtpPacketError.h + * @brief The ZRTP Error message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Error packet. + * + * The ZRTP simple message Error. The implementation sends this + * after detecting an error. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketError : public ZrtpPacketBase { + + protected: + Error_t* errorHeader; ///< Points to Error message + + public: + /// Creates a Error packet with default data + ZrtpPacketError(); + + /// Creates a Error packet from received data + ZrtpPacketError(uint8_t* data); + + virtual ~ZrtpPacketError(); + + /// Get the error code from Error message + uint32_t getErrorCode() { return ntohl(errorHeader->errorCode); }; + + /// Set error code in Error message + void setErrorCode(uint32_t code) {errorHeader->errorCode = htonl(code); }; + + private: + ErrorPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETERROR + diff --git a/src/libzrtpcpp/ZrtpPacketErrorAck.h b/src/libzrtpcpp/ZrtpPacketErrorAck.h new file mode 100644 index 0000000..e64c8a6 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketErrorAck.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2007 - 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETERRORACK_H_ +#define _ZRTPPACKETERRORACK_H_ + +/** + * @file ZrtpPacketErrorAck.h + * @brief The ZRTP ErrorAck message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the ErrorAck packet. + * + * The ZRTP simple message ErrorAck. The implementation sends this + * after receiving and checking the Error message. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZrtpPacketErrorAck : public ZrtpPacketBase { + + public: + /// Creates a ErrorAck packet with default data + ZrtpPacketErrorAck(); + + /// Creates a ErrorAck packet from received data + ZrtpPacketErrorAck(uint8_t* data); + virtual ~ZrtpPacketErrorAck(); + + private: + ErrorAckPacket_t data; +}; + +/** + * @} + */ +#endif // _ZRTPPACKETERRORACK_H_ diff --git a/src/libzrtpcpp/ZrtpPacketGoClear.h b/src/libzrtpcpp/ZrtpPacketGoClear.h new file mode 100644 index 0000000..10c3be6 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketGoClear.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETGOCLEAR_H_ +#define _ZRTPPACKETGOCLEAR_H_ + +/** + * @file ZrtpPacketGoClear.h + * @brief The ZRTP GoClear message + * + * GNU ZRTP does not implement GoClear feature + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the GoClear packet. + * + * The ZRTP message GoClear. The implementation sends this + * to order the peer to switch to clear mode (non-SRTP mode). + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketGoClear : public ZrtpPacketBase { + + protected: + GoClear_t* clearHeader; + + public: + /// Creates a GoCLear packet with default data + ZrtpPacketGoClear(); + + /// Creates a GoClear packet from received data + ZrtpPacketGoClear(uint8_t* data); + + virtual ~ZrtpPacketGoClear(); + + /// Not used + const uint8_t* getClearHmac() { return clearHeader->clearHmac; }; + + /// Not used + void setClearHmac(uint8_t *text) { memcpy(clearHeader->clearHmac, text, 32); }; + + /// Not used + void clrClearHmac() { memset(clearHeader->clearHmac, 0, 32); }; + + private: + GoClearPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETGOCLEAR + diff --git a/src/libzrtpcpp/ZrtpPacketHello.h b/src/libzrtpcpp/ZrtpPacketHello.h new file mode 100644 index 0000000..0cc7403 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketHello.h @@ -0,0 +1,191 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETHELLO_H_ +#define _ZRTPPACKETHELLO_H_ + +/** + * @file ZrtpPacketHello.h + * @brief The ZRTP Hello message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Hello packet. + * + * The ZRTP Hello message. The implementation sends this + * to start the ZRTP negotiation sequence. The Hello message + * offers crypto methods and parameters to the other party. The + * other party selects methods and parameters it can support + * and uses the Commit message to commit these. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketHello : public ZrtpPacketBase { + + protected: + Hello_t* helloHeader; ///< Point to the Hello message part + + int32_t nHash, ///< number of hash algorithms offered + nCipher, ///< number of cipher algorithms offered + nPubkey, ///< number of key agreement algorithms offered + nSas, ///< number of SAS algorithms offered + nAuth; ///< number of SRTP authentication algorithms offered + + int32_t oHash, ///< offsets in bytes to hash algorithm names + oCipher, ///< offsets in bytes to cipher algorithm names + oPubkey, ///< offsets in bytes to key agreement algorithm names + oSas, ///< offsets in bytes to SAS algorithm names + oAuth, ///< offsets in bytes to SRTP authentication algorithm names + oHmac; ///< offsets in bytes to MAC of Hello message + + public: + /// Creates a Hello packet with default data + ZrtpPacketHello(); + + /// Creates a Hello packet from received data + ZrtpPacketHello(uint8_t *data); + + virtual ~ZrtpPacketHello(); + + /** + * Set configure data and populate Hello message data. + * + * Fill in the offered Algorithm names and compute all offset to + * names and MAC. An application must call this method on Hello message + * objects created with the standard constructor (with default data) + * before the application can use most of the getter and setter methods. + * + * @param config + * Pointer to ZrtpConfigure data. + */ + void configureHello(ZrtpConfigure* config); + + /// Get version number from Hello message, fixed ASCII character array + uint8_t* getVersion() { return helloHeader->version; }; + + /// Get client id from Hello message, fixed ASCII character array + uint8_t* getClientId() { return helloHeader->clientId; }; + + /// Get H3 hash from Hello message, fixed byte array + uint8_t* getH3() { return helloHeader->hashH3; }; + + /// Get client ZID from Hello message, fixed bytes array + uint8_t* getZid() { return helloHeader->zid; }; + + /// Set version sting in Hello message, fixed ASCII character array + void setVersion(uint8_t *text) { memcpy(helloHeader->version, text,ZRTP_WORD_SIZE ); } + + /// Set client id in Hello message, fixed ASCII character array + void setClientId(const uint8_t *t) { memcpy(helloHeader->clientId, t, sizeof(helloHeader->clientId)); } + + /// Set H3 hash in Hello message, fixed byte array + void setH3(uint8_t *hash) { memcpy(helloHeader->hashH3, hash, sizeof(helloHeader->hashH3)); } + + /// Set client ZID in Hello message, fixed bytes array + void setZid(uint8_t *text) { memcpy(helloHeader->zid, text, sizeof(helloHeader->zid)); } + + /// Check passive mode (mode not implemented) + bool isPassive() { return helloHeader->flags & 0x10; }; + + /// Check if MitM flag is set + bool isMitmMode() { return helloHeader->flags & 0x20; }; + + /// Check if SAS sign flag is set + bool isSasSign() { return helloHeader->flags & 0x40; }; + + /// Get hash algorithm name at position n, fixed ASCII character array + uint8_t* getHashType(int32_t n) { return ((uint8_t*)helloHeader)+oHash+(n*ZRTP_WORD_SIZE); } + + /// Get ciper algorithm name at position n, fixed ASCII character array + uint8_t* getCipherType(int32_t n) { return ((uint8_t*)helloHeader)+oCipher+(n*ZRTP_WORD_SIZE); } + + /// Get SRTP authentication algorithm name at position n, fixed ASCII character array + uint8_t* getAuthLen(int32_t n) { return ((uint8_t*)helloHeader)+oAuth+(n*ZRTP_WORD_SIZE); } + + /// Get key agreement algorithm name at position n, fixed ASCII character array + uint8_t* getPubKeyType(int32_t n) { return ((uint8_t*)helloHeader)+oPubkey+(n*ZRTP_WORD_SIZE); } + + /// Get SAS algorithm name at position n, fixed ASCII character array + uint8_t* getSasType(int32_t n) { return ((uint8_t*)helloHeader)+oSas+(n*ZRTP_WORD_SIZE); } + + /// Get Hello MAC, fixed byte array + uint8_t* getHMAC() { return ((uint8_t*)helloHeader)+oHmac; } + + /// Set hash algorithm name at position n, fixed ASCII character array + void setHashType(int32_t n, int8_t* t) + { memcpy(((uint8_t*)helloHeader)+oHash+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } + + /// Set ciper algorithm name at position n, fixed ASCII character array + void setCipherType(int32_t n, int8_t* t) + { memcpy(((uint8_t*)helloHeader)+oCipher+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } + + /// Set SRTP authentication algorithm name at position n, fixed ASCII character array + void setAuthLen(int32_t n, int8_t* t) + { memcpy(((uint8_t*)helloHeader)+oAuth+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } + + /// Set key agreement algorithm name at position n, fixed ASCII character array + void setPubKeyType(int32_t n, int8_t* t) + { memcpy(((uint8_t*)helloHeader)+oPubkey+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } + + /// Set SAS algorithm name at position n, fixed ASCII character array + void setSasType(int32_t n, int8_t* t) + { memcpy(((uint8_t*)helloHeader)+oSas+(n*ZRTP_WORD_SIZE), t, ZRTP_WORD_SIZE); } + + /// Set Hello MAC, fixed byte array + void setHMAC(uint8_t* t) + { memcpy(((uint8_t*)helloHeader)+oHmac, t, 2*ZRTP_WORD_SIZE); } + + /// Get number of offered hash algorithms + int32_t getNumHashes() {return nHash; } + + /// Get number of offered cipher algorithms + int32_t getNumCiphers() {return nCipher; } + + /// Get number of offered key agreement algorithms + int32_t getNumPubKeys() {return nPubkey; } + + /// Get number of offered SAS algorithms + int32_t getNumSas() {return nSas; } + + /// Get number of offered SRTP authentication algorithms + int32_t getNumAuth() {return nAuth; } + + /// set MitM flag + void setMitmMode() { helloHeader->flags |= 0x20; } + + /// set SAS sign flag + void setSasSign() { helloHeader->flags |= 0x40; } + + private: + // Hello packet is of variable length. It maximum size is 46 words: + // - 11 words fixed sizze + // - up to 35 words variable part, depending on number of algorithms + // leads to a maximum of 4*46=184 bytes. + uint8_t data[256]; // large enough to hold a full blown Hello packet +}; + +/** + * @} + */ +#endif // ZRTPPACKETHELLO + diff --git a/src/libzrtpcpp/ZrtpPacketHelloAck.h b/src/libzrtpcpp/ZrtpPacketHelloAck.h new file mode 100644 index 0000000..345a071 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketHelloAck.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETHELLOACK_H_ +#define _ZRTPPACKETHELLOACK_H_ + +/** + * @file ZrtpPacketHelloAck.h + * @brief The ZRTP HelloAck message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the HelloAck packet. + * + * The ZRTP simple message HelloAck. The implementation sends this + * after receiving a Hello packet. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketHelloAck : public ZrtpPacketBase { + + public: + /// Creates a HelloAck packet with default data + ZrtpPacketHelloAck(); + + /// Creates a HelloAck packet from received data + ZrtpPacketHelloAck(uint8_t* data); + + virtual ~ZrtpPacketHelloAck(); + + private: + HelloAckPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETHELLOACK + diff --git a/src/libzrtpcpp/ZrtpPacketPing.h b/src/libzrtpcpp/ZrtpPacketPing.h new file mode 100644 index 0000000..840df62 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketPing.h @@ -0,0 +1,67 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETPING_H_ +#define _ZRTPPACKETPING_H_ + +/** + * @file ZrtpPacketPing.h + * @brief The ZRTP Ping message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the PingAck packet. + * + * The ZRTP simple message PingAck. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZrtpPacketPing : public ZrtpPacketBase { + + protected: + Ping_t* pingHeader; ///< Point the the Ping message + + public: + /// Creates a Ping message with default data + ZrtpPacketPing(); + + /// Creates a Ping message from received data + ZrtpPacketPing(uint8_t* data); + + virtual ~ZrtpPacketPing(); + + /// Set ZRTP protocol version field, fixed ASCII character array + void setVersion(uint8_t *text) { memcpy(pingHeader->version, text,ZRTP_WORD_SIZE ); } + + /// Get the endpoit hash, fixed byte array + uint8_t* getEpHash() { return pingHeader->epHash; } + + private: + PingPacket_t data; +}; + +/** + * @} + */ + +#endif // ZRTPPACKETCLEARACK + diff --git a/src/libzrtpcpp/ZrtpPacketPingAck.h b/src/libzrtpcpp/ZrtpPacketPingAck.h new file mode 100644 index 0000000..51ad8c8 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketPingAck.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETPINGACK_H_ +#define _ZRTPPACKETPINGACK_H_ + +#include <libzrtpcpp/ZrtpPacketBase.h> +/** + * @file ZrtpPacketPingAck.h + * @brief The ZRTP PingAck message + * + * @ingroup GNU_ZRTP + * @{ + */ + +/** + * Implement the PingAck packet. + * + * The ZRTP simple message PingAck. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZrtpPacketPingAck : public ZrtpPacketBase { + + protected: + PingAck_t* pingAckHeader; ///< Points to PingAck message + + public: + /// Creates a PingAck message with default data + ZrtpPacketPingAck(); + + /// Creates a PingAck message from received data + ZrtpPacketPingAck(uint8_t* data); + + virtual ~ZrtpPacketPingAck(); + + /// Get SSRC from PingAck message + uint32_t getSSRC() { return ntohl(pingAckHeader->ssrc); }; + + /// Set ZRTP protocol version field, fixed ASCII character array + void setVersion(uint8_t *text) { memcpy(pingAckHeader->version, text, ZRTP_WORD_SIZE ); } + + /// Set SSRC in PingAck message + void setSSRC(uint32_t data) {pingAckHeader->ssrc = htonl(data); }; + + /// Set remote endpoint hash, fixed byte array + void setRemoteEpHash(uint8_t *hash) { memcpy(pingAckHeader->remoteEpHash, hash, sizeof(pingAckHeader->remoteEpHash)); } + + /// Set local endpoint hash, fixed byte array + void setLocalEpHash(uint8_t *hash) { memcpy(pingAckHeader->localEpHash, hash, sizeof(pingAckHeader->localEpHash)); } + + private: + PingAckPacket_t data; +}; + +/** + * @} + */ +#endif // ZRTPPACKETCLEARACK + diff --git a/src/libzrtpcpp/ZrtpPacketRelayAck.h b/src/libzrtpcpp/ZrtpPacketRelayAck.h new file mode 100644 index 0000000..93437e6 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketRelayAck.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2007 - 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETRELAYACK_H_ +#define _ZRTPPACKETRELAYACK_H_ + +/** + * @file ZrtpPacketRelayAck.h + * @brief The ZRTP RelayAck message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the RelayAck packet. + * + * The ZRTP simple message RelayAck. The implementation sends this + * after receiving and checking the SASrelay message. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +class __EXPORT ZrtpPacketRelayAck : public ZrtpPacketBase { + + public: + /// Creates a RelayAck packet with default data + ZrtpPacketRelayAck(); + + /// Creates a RelayAck packet from received data + ZrtpPacketRelayAck(uint8_t* data); + virtual ~ZrtpPacketRelayAck(); + + private: + RelayAckPacket_t data; +}; + +/** + * @} + */ +#endif // _ZRTPPACKETRELAYACK_H_ diff --git a/src/libzrtpcpp/ZrtpPacketSASrelay.h b/src/libzrtpcpp/ZrtpPacketSASrelay.h new file mode 100644 index 0000000..427ac28 --- /dev/null +++ b/src/libzrtpcpp/ZrtpPacketSASrelay.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2006-2011 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPPACKETSASRELAY_H_ +#define _ZRTPPACKETSASRELAY_H_ + +/** + * @file ZrtpPacketSASrelay.h + * @brief The ZRTP SAS Relay message + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * Implement the Confirm packet. + * + * The ZRTP message Confirm. The implementation sends this + * to confirm the switch to SRTP (encrypted) mode. The contents of + * the Confirm message are encrypted, thus the implementation + * can check if the secret keys work. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpPacketSASrelay : public ZrtpPacketBase { + + private: + SASrelay_t* sasRelayHeader; ///< Point to the Confirm message part + + public: + /// Creates a Confirm packet with default data + ZrtpPacketSASrelay(); + + /// Creates a Confirm packet with default data and a given signature length + ZrtpPacketSASrelay(uint32_t sl); + + /// Creates a Confirm packet from received data + ZrtpPacketSASrelay(uint8_t* d); + + /// Normal destructor + virtual ~ZrtpPacketSASrelay(); + + /// Check is SAS verify flag is set + const bool isSASFlag() { return sasRelayHeader->flags & 0x4; } + + /// Get pointer to filler bytes (contains one bit of signature length) + const uint8_t* getFiller() { return sasRelayHeader->filler; } + + /// Get pointer to IV data, fixed byte array + const uint8_t* getIv() { return sasRelayHeader->iv; } + + /// Get pointer to MAC data, fixed byte array + const uint8_t* getHmac() { return sasRelayHeader->hmac; } + + /// Get pointer to new SAS rendering algorithm, fixed byte array + const uint8_t* getSas() {return sasRelayHeader->sas; } + + /// Get pointer to new SAS hash data, fixed byte array + const uint8_t* getTrustedSas() { return sasRelayHeader->trustedSasHash; } + + /// get the signature length in words + uint32_t getSignatureLength(); + + /// set SAS verified flag + void setSASFlag() { sasRelayHeader->flags |= 0x4; } + + /// Set MAC data, fixed length byte array + void setHmac(uint8_t* text) { memcpy(sasRelayHeader->hmac, text, sizeof(sasRelayHeader->hmac)); } + + /// Set IV data, fixed length byte array + void setIv(uint8_t* text) { memcpy(sasRelayHeader->iv, text, sizeof(sasRelayHeader->iv)); } + + /// Set SAS rendering algorithm, fixed length byte array + void setSas(uint8_t* text) { memcpy(sasRelayHeader->sas, text, sizeof(sasRelayHeader->sas)); } + + /// Set SAS hash data, fixed length byte array + void setTrustedSas(uint8_t* text) { memcpy(sasRelayHeader->trustedSasHash, text, sizeof(sasRelayHeader->trustedSasHash)); } + + /// Set signature length in words + void setSignatureLength(uint32_t sl); + + private: + void initialize(); + // Confirm packet is of variable length. It maximum size is 524 words: + // - 11 words fixed size + // - up to 513 words variable part, depending if signature is present and its length. + // This leads to a maximum of 4*524=2096 bytes. + uint8_t data[2100]; // large enough to hold a full blown Confirm packet + +}; + +/** + * @} + */ +#endif // ZRTPPACKETSASRELAY + diff --git a/src/libzrtpcpp/ZrtpQueue.h b/src/libzrtpcpp/ZrtpQueue.h new file mode 100644 index 0000000..0454771 --- /dev/null +++ b/src/libzrtpcpp/ZrtpQueue.h @@ -0,0 +1,917 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPQUEUE_H_ +#define _ZRTPQUEUE_H_ + +#include <ccrtp/cqueue.h> +#include <ccrtp/rtppkt.h> +#include <libzrtpcpp/ZrtpCallback.h> +#include <libzrtpcpp/TimeoutProvider.h> +#include <libzrtpcpp/ZrtpConfigure.h> + +class __EXPORT ZrtpUserCallback; +class __EXPORT ZRtp; + +NAMESPACE_COMMONCPP + +/** + * GNU ccRTP extension to support GNU ZRTP. + * + * ZRTP was developed by Phil Zimmermann and provides functions to + * negotiate keys and other necessary data (crypto data) to set-up + * the Secure RTP (SRTP) crypto context. Refer to Phil's ZRTP + * specification at his <a href="http://zfoneproject.com/">Zfone + * project</a> site to get more detailed imformation about the + * capabilities of ZRTP. + * + * <b>Short overview of the ZRTP implementation</b> + * + * ZRTP is a specific protocol to negotiate encryption algorithms + * and the required key material. ZRTP uses a RTP session to + * exchange its protocol messages. + * + * A complete GNU ZRTP implementation consists of two parts, the + * GNU ZRTP core and specific code that binds the GNU ZRTP core to + * the underlying RTP/SRTP stack and the operating system: + * <ul> + * <li> + * The GNU ZRTP core is independent of a specific RTP/SRTP + * stack and the operationg system and consists of the ZRTP + * protocol state engine, the ZRTP protocol messages, and the + * GNU ZRTP engine. The GNU ZRTP engine provides methods to + * setup ZRTP message and to analyze received ZRTP messages, + * to compute the crypto data required for SRTP, and to + * maintain the required hashes and HMAC. + * </li> + * <li> + * The second part of an implementation is specific + * <em>glue</em> code the binds the GNU ZRTP core to the + * actual RTP/SRTP implementation and other operating system + * specific services such as timers. + * </li> + * </ul> + * + * The GNU ZRTP core uses a callback interface class (refer to + * ZrtpCallback) to access RTP/SRTP or operating specific methods, + * for example to send data via the RTP/SRTP stack, to access + * timers, provide mutex handling, and to report events to the + * application. + * + * <b>The ZrtpQueue</b> + * + * ZrtpQueue implements code that is specific to the GNU ccRTP + * implementation. ZrtpQueue also implements the specific code to + * provide the mutex and timeout handling to the GNU ZRTP + * core. Both, the mutex and the timeout handling, use the GNU + * Common C++ library to stay independent of the operating + * seystem. For more information refer to the <a + * href="http://www.gnutelephony.org/index.php/GNU_Common_C%2B%2B">GNU + * Common C++</a> web site. + * + * To perform its tasks ZrtpQueue + * <ul> + * <li> extends GNU ccRTP classes to use the underlying + * ccRTP methods and the RTP/SRTP send and receive queues + * </li> + * <li> implements the ZrtpCallback interface to provide ccRTP + * access and other specific services (timer, mutex) to GNU + * ZRTP + * </li> + * <li> provides ZRTP specific methods that applications may use + * to control and setup GNU ZRTP + * </li> + * <li> can register and use an application specific callback + * class (refer to ZrtpUserCallback) + * </li> + * </ul> + * + * After instantiating a GNU ZRTP session (see below for a short + * example) applications may use the ZRTP specific methods of + * ZrtpQueue to control and setup GNU ZRTP, for example enable or + * disable ZRTP processing or getting ZRTP status information. + * + * GNU ZRTP provides a ZrtpUserCallback class that an application + * may extend and register with ZrtpQueue. GNU ZRTP and ZrtpQueue + * use the ZrtpUserCallback methods to report ZRTP events to the + * application. The application may display this information to + * the user or act otherwise. + * + * The following figure depicts the relationships between + * ZrtpQueue, ccRTP RTP/SRTP implementation, the GNU ZRTP core, + * and an application that provides an ZrtpUserCallback class. + * +@verbatim + + +----------+ + | ccRTP | + | RTP/SRTP | + | | + +----------+ + ^ + | extends + | ++----------------+ +-----+------+ +| Application | | | +-----------------+ +| instantiates | uses | ZrtpQueue | uses | | +| a ZRTP Session +------+ implements +------+ GNU ZRTP | +| and provides | |ZrtpCallback| | core | +|ZrtpUserCallback| | | | implementation | ++----------------+ +------------+ | (ZRtp et al) | + | | + +-----------------+ +@endverbatim + * + * Because ZrtpQueue extends the ccRTP RTP/SRTP implementation + * (AVPQueue) all public methods defined by ccRTP are also + * available for a ZRTP session. ZrtpQueue overwrites some of the + * public methods of ccRTP (AVPQueue) to implement ZRTP specific + * code. + * + * GNU ZRTP provides a <em>SymmetricZRTPSession</em> type to + * simplify its use. An application uses this type in the same way + * as it would use the normal ccRTP <em>SymmetricRTPSession</em> + * type. The following short code snippets show how an application + * could instantiate ccRTP and GNU ZRTP sessions. The first + * snippet shows how to instantiate a ccRTP session: + * + * @code + * ... + * #include <ccrtp/rtp.h> + * ... + * SymmetricRTPSession tx(pattern.getSsrc(), + * InetHostAddress("localhost")); + * ... + * + * @endcode + * + * The same code as above but using a GNU ZRTP session this time: + * @code + * ... + * #include <libzrtpcpp/zrtpccrtp.h> + * ... + * SymmetricZRTPSession tx(pattern.getSsrc(), + * InetHostAddress("localhost")); + * ... + * + * @endcode + * + * The only differences are the different include statements and + * the different session types. + * + * The <em>demo</em> folder contains a small example that shows + * how to use GNU ZRTP. + * + * Please refer to the GNU ccRTP documentation for a description + * of ccRTP methods and functions. This ZrtpQueue documentation + * shows the ZRTP specific extensions and describes overloaded + * methods and a possible different behaviour. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpQueue : public AVPQueue, ZrtpCallback { + +public: + + /** + * Initialize the ZrtpQueue. + * + * Before an application can use ZRTP it has to initialize the + * ZRTP implementation. This method initializes the timeout + * thread and opens a file that contains ZRTP specific + * information such as the applications ZID (ZRTP id) and its + * retained shared secrets. + * + * If one application requires several ZRTP sessions all + * sessions use the same timeout thread and use the same ZID + * file. Therefore an application does not need to do any + * synchronisation regading ZID files or timeouts. This is + * managed by the ZRTP implementation. + * + * The current implementation of ZrtpQueue does not support + * different ZID files for one application instance. This + * restriction may be removed in later versions. + * + * The application may specify its own ZID file name. If no + * ZID file name is specified it defaults to + * <code>$HOME/.GNUccRTP.zid</code> if the <code>HOME</code> + * environment variable is set. If it is not set the current + * directory is used. + * + * If the method could set up the timeout thread and open the ZID + * file then it enables ZRTP processing and returns. + * + * @param zidFilename + * The name of the ZID file, can be a relative or absolut + * filename. + * + * @param autoEnable + * if set to true the method automatically sets enableZrtp to + * true. This enables the ZRTP auto-sense mode. Default is true. + * + * @param config + * this parameter points to ZRTP configuration data. If it is + * NULL then ZrtpQueue uses a default setting. Default is NULL. + * + * @return + * 1 on success, ZRTP processing enabled, -1 on failure, + * ZRTP processing disabled. + * + */ + int32_t initialize(const char *zidFilename, bool autoEnable = true, + ZrtpConfigure* config = NULL); + + /* + * Applications use the following methods to control ZRTP, for example + * to enable ZRTP, set flags etc. + */ + + /** + * Enable or disable ZRTP processing. + * + * Call this method to enable or disable ZRTP processing after + * calling <code>initialize()</code>. This can be done before + * using a RTP session or at any time during a RTP session. + * + * Existing SRTP sessions or currently active ZRTP processing will + * not be stopped or disconnected. + * + * If the application enables ZRTP then: + * <ul> + * <li>ZrtpQueue starts to send ZRTP Hello packets after at least + * one RTP packet was sent and received on the associated RTP + * session. Thus if an application enables ZRTP and ZrtpQueue + * detects traffic on the RTP session then ZrtpQueue automatically + * starts the ZRTP protocol. This automatic start is convenient + * for applications that negotiate RTP parameters and set up RTP + * sessions but the actual RTP traffic starts some time later. + * </li> + * <li>ZrtpQueue analyses incoming packets to detect ZRTP + * messages. If ZRTP was started, either via automatic start (see + * above) or explicitly via startZrtp(), then ZrtpQueue + * forwards ZRTP packets to the GNU ZRTP core. + * </ul> + * + * @param onOff + * @c true to enable ZRTP, @c false to disable ZRTP + */ + void setEnableZrtp(bool onOff); + + /** + * Return the state of ZRTP enable state. + * + * @return @c true if ZRTP processing is enabled, @c false + * otherwise. + */ + bool isEnableZrtp(); + + /** + * Set SAS as verified. + * + * The application may call this method if the user confirmed + * (verfied) the Short Authentication String (SAS) with the peer. + * + * ZRTP calls ZrtpUserCallback#showSAS after it computed the SAS + * and the application registered a user callback class. The + * application should display the SAS and provide a mechanism at + * the user interface that enables the user to confirm the SAS. + * + * ZRTP remembers the SAS confirmation status together with the + * retained secrets data. If both parties confirmed the SAS then + * ZRTP informs the application about this status on the next ZRTP + * session. + * + * For more detailed information regarding SAS please refer to the + * ZRTP specification, chapter 8. + */ + void SASVerified(); + + /** + * Reset the SAS verfied flag for the current user's retained secrets. + * + */ + void resetSASVerified(); + + /** + * To confirm a go clear request. + * + * Call this method if the user confirmed a go clear (secure mode off). + */ + void goClearOk(); + + /** + * Request to switch off secure mode. + * + * Call this method is the user itself wants to switch off secure + * mode (go clear). After sending the "go clear" request to the peer + * ZRTP immediatly switch off SRTP processing. Every RTP data is sent + * in clear after the go clear request. + */ + void requestGoClear(); + + /** + * Set the auxilliary secret. + * + * Use this method to set the srtps secret data. Refer to ZRTP + * specification, chapter 5.3 ff + * + * @param data + * Points to the auxilliary secret data. + * @param length + * Length of the auxilliary secrect in bytes + */ + void setAuxSecret(uint8_t* data, int32_t length); + + /** + * Set the application's callback class. + * + * The destructor of ZrtpQueue also destorys the user callback + * class if it was set. The application must not delete the + * callback object or use/reference the callback object after + * ZrtpQueue was destroyed. + * + * @param ucb + * Implementation of the application's ZrtpUserCallback class + */ + void setUserCallback(ZrtpUserCallback* ucb); + + /** + * Set the client ID for ZRTP Hello message. + * + * The GNU ccRTP client may set its id to identify itself in the + * ZRTP Hello message. The maximum length is 16 characters. A + * shorter id string is possible, it will be filled with blanks. A + * longer id string will be truncated to 16 characters. The + * standard client id is <code>'GNU ccRTP ZRTP '</code> (without + * the quotes). + * + * Setting the client's id must be done before calling + * ZrtpQueue#initialize() or ZrtpQueue#startZrtp() . + * + * @param id + * The client's id string + */ + void setClientId(std::string id); + + /** + * Get the ZRTP Hello Hash data. + * + * Use this method to get the ZRTP Hello Hash data. The method + * returns the data as a string containing hex-digits. Refer + * to ZRTP specification, chapter 9.1. + * + * @return + * a std:string containing the Hello hash value as hex-digits. The + * hello hash is available immediatly after calling + * ZrtpQueue#startZrtp. If ZRTP was not started the method returns + * an empty string. + */ + std::string getHelloHash(); + + /** + * Get the peer's ZRTP Hello Hash data. + * + * Use this method to get the peer's ZRTP Hello Hash data. The method + * returns the data as a string containing the ZRTP protocol version and + * hex-digits. + * + * The peer's hello hash is available only after ZRTP received a hello. If + * no data is available the function returns an empty string. + * + * Refer to ZRTP specification, chapter 8. + * + * @return + * a std:string containing the Hello version and the hello hash as hex digits. + */ + std::string getPeerHelloHash(); + + /** + * Get Multi-stream parameters. + * + * Use this method to get the Multi-stream that were computed during + * the ZRTP handshake. An application may use these parameters to + * enable multi-stream processing for an associated SRTP session. + * + * Refer to chapter 5.4.2 in the ZRTP specification for further details + * and restriction how and when to use multi-stream mode. + * + * @return + * a string that contains the multi-stream parameters. The application + * must not modify the contents of this string, it is opaque data. The + * application may hand over this string to a new ZrtpQueue instance + * to enable multi-stream processing for this ZrtpQueue. If ZRTP was + * not started or ZRTP is not yet in secure state the method returns an + * empty string. + * + * @see setMultiStrParams() + */ + std::string getMultiStrParams(); + + /** + * Set Multi-stream parameters. + * + * Use this method to set the parameters required to enable Multi-stream + * processing of ZRTP. The multi-stream parameters must be set before the + * application starts the ZRTP protocol engine. + * + * Refer to chapter 5.4.2 in the ZRTP specification for further details + * of multi-stream mode. + * + * @param parameters + * A string that contains the multi-stream parameters that this + * new ZrtpQueue instanace shall use. + * + * @see getMultiStrParams() + */ + void setMultiStrParams(std::string parameters); + + /** + * Check if this ZRTP use Multi-stream. + * + * Use this method to check if this ZRTP instance uses multi-stream. Even + * if the application provided multi-stram parameters it may happen that + * full DH mode was used. Refer to chapters 5.2 and 5.4.2 in the ZRTP # + * when this may happen. + * + * @return + * True if multi-stream is used, false otherwise. + */ + bool isMultiStream(); + + /** + * Check if the other ZRTP client supports Multi-stream. + * + * Use this method to check if the other ZRTP client supports + * Multi-stream mode. + * + * @return + * True if multi-stream is available, false otherwise. + */ + bool isMultiStreamAvailable(); + + /** + * Accept a PBX enrollment request. + * + * If a PBX service asks to enroll the MiTM key and the user accepts this + * requtes, for example by pressing an OK button, the client application + * shall call this method and set the parameter <code>accepted</code> to + * true. If the user does not accept the request set the parameter to + * false. + * + * @param accepted + * True if the enrollment request is accepted, false otherwise. + */ + void acceptEnrollment(bool accepted); + + /** + * Get the commited SAS rendering algorithm for this ZRTP session. + * + * @return the commited SAS rendering algorithm + */ + std::string getSasType(); + + /** + * Get the computed SAS hash for this ZRTP session. + * + * A PBX ZRTP back-to-Back function uses this function to get the SAS + * hash of an enrolled client to construct the SAS relay packet for + * the other client. + * + * @return a refernce to the byte array that contains the full + * SAS hash. + */ + uint8_t* getSasHash(); + + /** + * Send the SAS relay packet. + * + * The method creates and sends a SAS relay packet according to the ZRTP + * specifications. Usually only a MitM capable user agent (PBX) uses this + * function. + * + * @param sh the full SAS hash value + * @param render the SAS rendering algorithm + */ + bool sendSASRelayPacket(uint8_t* sh, std::string render); + + /** + * Check the state of the MitM mode flag. + * + * If true then this ZRTP session acts as MitM, usually enabled by a PBX + * client (user agent) + * + * @return state of mitmMode + */ + bool isMitmMode(); + + /** + * Set the state of the MitM mode flag. + * + * If MitM mode is set to true this ZRTP session acts as MitM, usually + * enabled by a PBX client (user agent). + * + * @param mitmMode defines the new state of the mitmMode flag + */ + void setMitmMode(bool mitmMode); + + /** + * Enable or disable paranoid mode. + * + * The Paranoid mode controls the behaviour and handling of the SAS verify flag. If + * Panaoid mode is set to flase then ZRtp applies the normal handling. If Paranoid + * mode is set to true then the handling is: + * + * <ul> + * <li> always set the SAS verify flag to <code>false</code> at srtpSecretsOn() callback. The + * user interface (UI) must show <b>SAS not verified</b>. See implementation note below.</li> + * <li> don't set the SAS verify flag in the <code>Confirm</code> packets, thus forcing the other + * peer to report <b>SAS not verified</b>.</li> + * <li> ignore the <code>SASVerified()</code> function, thus do not set the SAS verified flag + * in the ZRTP cache. </li> + * <li> Disable the <em>Trusted PBX MitM</em> feature. Just send the <code>SASRelay</code> packet + * but do not process the relayed data. This protects the user from a malicious + * "trusted PBX".</li> + * </ul> + * ZRtp performs alls other steps during the ZRTP negotiations as usual, in particular it + * computes, compares, uses, and stores the retained secrets. This avoids unnecessary warning + * messages. The user may enable or disable the Paranoid mode on a call-by-call basis without + * breaking the key continuity data. + * + * <b>Implementation note:</b><br/> + * An application shall <b>always display the SAS if the SAS verify flag is <code>false</code></b>. + * The application shall remind the user to compare the SAS code, for example using larger fonts, + * different colours and other display features. + */ + void setParanoidMode(bool yesNo); + + /** + * Check status of paranoid mode. + * + * @return + * Returns true if paranoid mode is enabled. + */ + bool isParanoidMode(); + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and performs the enrollment actions. A MitM (PBX) enrollment service sets this flagstarted this ZRTP + * session. Can be set to true only if mitmMode is also true. + * @return status of the enrollmentMode flag. + */ + bool isEnrollmentMode(); + + /** + * Check the state of the enrollment mode. + * + * If true then we will set the enrollment flag (E) in the confirm + * packets and perform the enrollment actions. A MitM (PBX) enrollment + * service must sets this mode to true. + * + * Can be set to true only if mitmMode is also true. + * + * @param enrollmentMode defines the new state of the enrollmentMode flag + */ + void setEnrollmentMode(bool enrollmentMode); + + /** + * Backwards compatible api fix... + */ + inline void setPBXEnrollment(bool enrollmentMode) + {setMitmMode(enrollmentMode); setEnrollmentMode(enrollmentMode);} + + /** + * Check if a peer's cache entry has a vaild MitM key. + * + * If true then the other peer ha a valid MtiM key, i.e. the peer has performed + * the enrollment procedure. A PBX ZRTP Back-2-Back application can use this function + * to check which of the peers is enrolled. + * + * @return True if the other peer has a valid Mitm key (is enrolled). + */ + bool isPeerEnrolled(); + + /** + * Set the state of the SAS signature mode flag. + * + * If SAS signature mode is set to true this ZRTP session support SAS signature + * callbacks and signature transfer between clients. + * + * @param sasSignMode defines the new state of the sasSignMode flag + */ + void setSignSas(bool sasSignMode); + + /** + * Set signature data + * + * This functions stores signature data and transmitts it during ZRTP + * processing to the other party as part of the Confirm packets. Refer to + * chapters 6.7 and 8.2 in the ZRTP specification. + * + * @param data + * The signature data including the signature type block. The method + * copies this data into the Confirm packet at signature type block. + * @param length + * The length of the signature data in bytes. This length must be + * multiple of 4. + * @return + * True if the method stored the data, false otherwise. + */ + bool setSignatureData(uint8* data, int32 length); + + /** + * Get signature data + * + * This functions returns signature data that was receivied during ZRTP + * processing. Refer to chapters 6.7 and 8.2. + * + * @return + * Pointer to signature data. This is a pointer to volatile data that is + * only valid during the checkSASSignature() callback. The application + * shall copy the data if necessary. + */ + const uint8* getSignatureData(); + + /** + * Get length of signature data + * + * This functions returns the length of signature data that was receivied + * during ZRTP processing. Refer to chapters 6.7 and 8.2. + * + * @return + * Length in bytes of the received signature data. The method returns + * zero if no signature data avilable. + */ + int32 getSignatureLength(); + + /** + * Put data into the RTP output queue. + * + * This is used to create a data packet in the send queue. + * Sometimes a "NULL" or empty packet will be used instead, and + * these are known as "silent" packets. "Silent" packets are + * used simply to "push" the scheduler along more accurately + * by giving the appearence that a next packet is waiting to + * be sent and to provide a valid timestamp for that packet. + * + * This method overrides the same method in OutgoingDataQueue class. + * During ZRTP processing it may be necessary to control the + * flow of outgoing RTP payload packets (GoClear processing). + * + * @param stamp Timestamp for expected send time of packet. + * @param data Value or NULL if special "silent" packet. + * @param len May be 0 to indicate a default by payload type. + **/ + void + putData(uint32 stamp, const unsigned char* data = NULL, size_t len = 0); + + /** + * Immediatly send a data packet. + * + * This is used to create a data packet and send it immediately. + * Sometimes a "NULL" or empty packet will be used instead, and + * these are known as "silent" packets. "Silent" packets are + * used simply to "push" the scheduler along more accurately + * by giving the appearence that a next packet is waiting to + * be sent and to provide a valid timestamp for that packet. + * + * This method overrides the same method in OutgoingDataQueue + * class. During ZRTP processing it may be necessary to + * control the flow of outgoing RTP payload packets (GoClear + * processing). + * + * @param stamp Timestamp immediate send time of packet. + * @param data Value or NULL if special "silent" packet. + * @param len May be 0 to indicate a default by payload type. + **/ + void + sendImmediate(uint32 stamp, const unsigned char* data = NULL, size_t len = 0); + + /** + * Starts the ZRTP protocol engine. + * + * Applications may call this method to immediatly start the ZRTP protocol + * engine any time after initializing ZRTP and setting optinal parameters, + * for example client id or multi-stream parameters. + * + * If the application does not call this method but sucessfully initialized + * the ZRTP engine using <code>initialize()</code> then ZRTP also starts + * after the application sent and received RTP packets. An application can + * disable this automatic, delayed start of the ZRTP engine using + * <code>setEnableZrtp(false)</code> before sending or receiving RTP + * packets. + * + */ + void startZrtp(); + + /** + * Stops the ZRTP protocol engine. + * + * Applications call this method to stop the ZRTP protocol + * engine. + * + */ + void stopZrtp(); + + /** + * Get other party's ZID (ZRTP Identifier) data + * + * This functions returns the other party's ZID that was receivied + * during ZRTP processing. + * + * The ZID data can be retrieved after ZRTP receive the first Hello + * packet from the other party. The application may call this method + * for example during SAS processing in showSAS(...) user callback + * method. + * + * @param data + * Pointer to a data buffer. This buffer must have a size of + * at least 12 bytes (96 bit) (ZRTP Identifier, see chap. 4.9) + * @return + * Number of bytes copied into the data buffer - must be equivalent + * to 96 bit, usually 12 bytes. + */ + int32 getPeerZid(uint8* data); + +protected: + friend class TimeoutProvider<std::string, ost::ZrtpQueue*>; + + /** + * A hook that gets called if the decoding of an incoming SRTP + * was erroneous + * + * @param pkt + * The SRTP packet with error. + * @param errorCode + * The error code: -1 - SRTP authentication failure, -2 - replay + * check failed + * @return + * True: put the packet in incoming queue for further processing + * by the applications; false: dismiss packet. The default + * implementation returns false. + */ + virtual bool + onSRTPPacketError(IncomingRTPPkt& pkt, int32 errorCode); + + /** + * Handle timeout event forwarded by the TimeoutProvider. + * + * Just call the ZRTP engine for further processing. + */ + void handleTimeout(const std::string &c); + + /** + * This function is used by the service thread to process + * the next incoming packet and place it in the receive list. + * + * This class overloads the function of IncomingDataQueue + * implementation. + * + * @return number of payload bytes received, <0 if error. + */ + virtual size_t takeInDataPacket(); + + /* + * The following methods implement the GNU ZRTP callback interface. + * For detailed documentation refer to file ZrtpCallback.h + */ + int32_t sendDataZRTP(const unsigned char* data, int32_t length); + + int32_t activateTimer(int32_t time); + + int32_t cancelTimer(); + + void sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); + + bool srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part); + + void srtpSecretsOff(EnableSecurity part); + + void srtpSecretsOn(std::string c, std::string s, bool verified); + + void handleGoClear(); + + void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode); + + void zrtpNotSuppOther(); + + void synchEnter(); + + void synchLeave(); + + void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info); + + void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info); + + void signSAS(uint8_t* sasHash); + + bool checkSASSignature(uint8_t* sasHash); + + /* + * End of ZrtpCallback functions. + */ + + ZrtpQueue(uint32 size = RTPDataQueue::defaultMembersHashSize, + RTPApplication& app = defaultApplication()); + + /** + * Local SSRC is given instead of computed by the queue. + */ + ZrtpQueue(uint32 ssrc, uint32 size = + RTPDataQueue::defaultMembersHashSize, + RTPApplication& app = defaultApplication()); + + virtual ~ZrtpQueue(); + +private: + void init(); + size_t rtpDataPacket(unsigned char* packet, int32 rtn, + InetHostAddress network_address, + tpport_t transport_port); + + ZRtp *zrtpEngine; + ZrtpUserCallback* zrtpUserCallback; + + std::string clientIdString; + + bool enableZrtp; + + int32 secureParts; + + int16 senderZrtpSeqNo; + ost::Mutex synchLock; // Mutex for ZRTP (used by ZrtpStateClass) + uint32 peerSSRC; + bool started; + bool mitmMode; + bool signSas; + bool enableParanoidMode; +}; + +class IncomingZRTPPkt : public IncomingRTPPkt { + +public: + /** + * Build a ZRTP packet object from a data buffer. + * + * @param block pointer to the buffer the whole packet is stored in. + * @param len length of the whole packet, expressed in octets. + * + **/ + + IncomingZRTPPkt(const unsigned char* block, size_t len); + + ~IncomingZRTPPkt() + { } + + uint32 + getZrtpMagic() const; + + uint32 + getSSRC() const; +}; + +class OutgoingZRTPPkt : public OutgoingRTPPkt { + +public: + /** + * Construct a new ZRTP packet to be sent. + * + * A new copy in memory (holding all this components + * along with the fixed header) is created. + * + * @param hdrext whole header extension. + * @param hdrextlen size of whole header extension, in octets. + **/ + OutgoingZRTPPkt(const unsigned char* const hdrext, uint32 hdrextlen); + ~OutgoingZRTPPkt() + { } +}; + +END_NAMESPACE + +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/src/libzrtpcpp/ZrtpStateClass.h b/src/libzrtpcpp/ZrtpStateClass.h new file mode 100644 index 0000000..fba061d --- /dev/null +++ b/src/libzrtpcpp/ZrtpStateClass.h @@ -0,0 +1,324 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPSTATECLASS_H_ +#define _ZRTPSTATECLASS_H_ + +/** + * @file ZrtpStateClass.h + * @brief The ZRTP state handling class + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpStates.h> +#include <libzrtpcpp/ZrtpPacketBase.h> + +/** + * The ZRTP states + * + * Depending on the role of this state engine and the actual protocl flow + * not all states are processed during a ZRTP handshake. + */ +enum zrtpStates { + Initial, ///< Initial state after starting the state engine + Detect, ///< State sending Hello, try to detect answer message + AckDetected, ///< HelloAck received + AckSent, ///< HelloAck sent after Hello received + WaitCommit, ///< Wait for a Commit message + CommitSent, ///< Commit message sent + WaitDHPart2, ///< Wait for a DHPart2 message + WaitConfirm1, ///< Wait for a Confirm1 message + WaitConfirm2, ///< Wait for a confirm2 message + WaitConfAck, ///< Wait for Conf2Ack + WaitClearAck, ///< Wait for clearAck - not used + SecureState, ///< This is the secure state - SRTP active + WaitErrorAck, ///< Wait for ErrorAck message + numberOfStates ///< Gives total number of protocol states +}; + +enum EventReturnCodes { + Fail = 0, ///< ZRTP event processing failed. + Done = 1 ///< Event processing ok. +}; + +enum EventDataType { + ZrtpInitial = 1, ///< Initial event, enter Initial state + ZrtpClose, ///< Close event, shut down state engine + ZrtpPacket, ///< Normal ZRTP message event, process according to state + Timer, ///< Timer event + ErrorPkt ///< Error packet event +}; + +enum SecureSubStates { + Normal, + WaitSasRelayAck, + numberofSecureSubStates +}; + +/// A ZRTP state event +typedef struct Event { + EventDataType type; ///< Type of event + uint8_t* packet; ///< Event data if availabe, usually a ZRTP message +} Event_t; + + +/** + * The ZRTP timer structure. + * + * This structure holds all necessary data to compute the timer for + * the protocol timers. The state engine allocate one structure for + * each timer. ZRTP uses two timers, T1 and T2, to monitor protocol + * timeouts. As a slight misuse but to make overall handling a bit + * simpler this structure also contains the resend counter. This is + * possible in ZRTP because it uses a simple timeout strategy. + */ +typedef struct zrtpTimer { + int32_t time, ///< Current timeout value + start, ///< Start value for timeout + increment, ///< increment timeout after each timeout event (not used anymore) + capping, ///< Maximum timeout value + counter, ///< Current number of timeouts + maxResend; ///< Maximum number of timeout resends +} zrtpTimer_t; + + +class ZRtp; + +/** + * This class is the ZRTP protocol state engine. + * + * This class is responsible to handle the ZRTP protocol. It does not + * handle the ZRTP HMAC, DH, and other data management. This is done in + * class ZRtp, which is the parent of this class. + * + * The methods of this class implement the ZRTP state actions. + * + */ + + +class __EXPORT ZrtpStateClass { + +private: + ZRtp* parent; ///< The ZRTP implmentation + ZrtpStates* engine; ///< The state switching engine + Event_t* event; ///< Current event to process + + /** + * The last packet that was sent. + * + * If we are <code>Initiator</code> then resend this packet in case of + * timeout. + */ + ZrtpPacketBase* sentPacket; + + /** + * Points to prepared Commit packet after receiving a Hello packet + */ + ZrtpPacketCommit* commitPkt; + + zrtpTimer_t T1; ///< The Hello message timeout timer + zrtpTimer_t T2; ///< Timeout timer for other messages + + /* + * If this is set to true the protocol engine handle the multi-stream + * variant of ZRTP. Refer to chapter 5.4.2 in the ZRTP specification. + */ + bool multiStream; + + // Secure substate to handle SAS relay packets + SecureSubStates secSubstate; + + /** + * Secure Sub state WaitSasRelayAck. + * + * This state belongs to the secure substates and handles + * SAS Relay Ack. + * + * When entering this transition function + * - sentPacket contains Error packet, Error timer active + * + * Possible events in this state are: + * - timeout for sent SAS Relay packet: causes a resend check and repeat sending + * of packet + * - SASRelayAck: Stop timer and switch to secure substate Normal. + */ + bool subEvWaitRelayAck(); + +public: + /// Create a ZrtpStateClass + ZrtpStateClass(ZRtp *p); + ~ZrtpStateClass(); + + /// Check if in a specified state + bool inState(const int32_t state) { return engine->inState(state); }; + + /// Switch to the specified state + void nextState(int32_t state) { engine->nextState(state); }; + + /// Process an event, the main entry point into the state engine + void processEvent(Event_t *ev); + + /** + * The state event handling methods. + * + * Refer to the protocol state diagram for further documentation. + */ + /// Initial event state + void evInitial(); + + /// Detect state + void evDetect(); + + /// HelloAck detected state + void evAckDetected(); + + /// HelloAck sent state + void evAckSent(); + + /// Wait for Commit message + void evWaitCommit(); + + /// Commit sent state + void evCommitSent(); + + /// Wait for DHPart2 message + void evWaitDHPart2(); + + /// Wait for Confirm2 message + void evWaitConfirm1(); + + /// Wait for Confirm2 message + void evWaitConfirm2(); + + /// Wait for ConfAck message + void evWaitConfAck(); + + /// Wait for ClearAck message (not used) + void evWaitClearAck(); + + /// Secure reached state + void evSecureState(); + + /// Wait for ErrorAck message + void evWaitErrorAck(); + + /** + * Initialize and activate a timer. + * + * @param t + * The ZRTP timer structure to use for the timer. + * @return + * 1 timer was activated + * 0 activation failed + */ + int32_t startTimer(zrtpTimer_t *t); + + /** + * Compute and set the next timeout value. + * + * @param t + * The ZRTP timer structure to use for the timer. + * @return + * 1 timer was activated + * 0 activation failed + * -1 resend counter exceeded + */ + int32_t nextTimer(zrtpTimer_t *t); + + /** + * Cancel the active timer. + * + * @return + * 1 timer was canceled + * 0 cancelation failed + */ + int32_t cancelTimer() {return parent->cancelTimer(); }; + + /** + * Prepare and send an Error packet. + * + * Preparse an Error packet and sends it. It stores the Error + * packet in the sentPacket variable to enable resending. The + * method switches to protocol state Initial. + */ + void sendErrorPacket(uint32_t errorCode); + + /** + * Set status if an error occured while sending a ZRTP packet. + * + * This functions clears data and set the state to Initial after the engine + * detected a problem while sending a ZRTP packet. + * + * @return + * Fail code + */ + void sendFailed(); + + /** + * Set status if a timer problems occure. + * + * This functions clears data and set state to Initial after a timer + * error occured. Either no timer available or resend counter exceedeed. + * + * @return + * Fail code + */ + void timerFailed(int32_t subCode); + + /** + * Set multi-stream mode flag. + * + * This functions set the multi-stream mode. The protocol + * engine will run the multi-stream mode variant of the ZRTP + * protocol if this flag is set to true. + * + * @param multi + * Set the multi-stream mode flag to true or false. + */ + void setMultiStream(bool multi); + + /** + * Status of multi-stream mode flag. + * + * This functions returns the value of the multi-stream mode flag. + * + * @return + * Value of the multi-stream mode flag. + */ + bool isMultiStream(); + + /** + * Send a SAS relay packet. + * + * the functions stores sends the SAS relay packet and stores the pointer in + * the sentPacket variable to enable resending. + * + * The method switches to secure substate WaitSasRelayAck. + * + * @param relay + * Pointer to the SAS relay packet. + */ + void sendSASRelay(ZrtpPacketSASrelay* relay); +}; + +/** + * @} + */ +#endif // _ZRTPSTATECLASS_H_ + diff --git a/src/libzrtpcpp/ZrtpStates.h b/src/libzrtpcpp/ZrtpStates.h new file mode 100644 index 0000000..44662a0 --- /dev/null +++ b/src/libzrtpcpp/ZrtpStates.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _ZRTPSTATES_H_ +#define _ZRTPSTATES_H_ + +/** + * @file ZrtpStates.h + * @brief The ZRTP state switching class + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdint.h> + +class __EXPORT ZrtpStateClass; +/** + * This structure hold the state name as enum (int) number and the pointer to + * the functions that handles the various triggers that can occur in a state. + */ +typedef struct { + int32_t stateName; ///< The state number + void (ZrtpStateClass::* handler)(void); ///< The state handler +} state_t; + +/** + * Implement a simple state switching. + * + * This class provides functions that manage the states and the event handler + * functions. Its a very simple implementation. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class __EXPORT ZrtpStates { + public: + + /// Create an initialize state switching + ZrtpStates(state_t* const zstates, + const int32_t numStates, + const int32_t initialState): + numStates(numStates), states(zstates), state(initialState) {} + + /// Call a state handler + int32_t processEvent(ZrtpStateClass& zsc) { + (zsc.*states[state].handler)(); + return 0; + } + + /// Check if in specified state + bool inState(const int32_t s) { return ((s == state)); } + + /// Set the next state + void nextState(int32_t s) { state = s; } + + private: + const int32_t numStates; + const state_t* states; + int32_t state; + + ZrtpStates(); +}; + +/** + * @} + */ +#endif //ZRTPSTATES + diff --git a/src/libzrtpcpp/ZrtpTextData.h b/src/libzrtpcpp/ZrtpTextData.h new file mode 100644 index 0000000..e79cb98 --- /dev/null +++ b/src/libzrtpcpp/ZrtpTextData.h @@ -0,0 +1,124 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _ZRTPTEXTDATA_H_ +#define _ZRTPTEXTDATA_H_ + +/** + * @file ZrtpTextData.h + * @brief The ZRTP ASCII texts - extern references + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <libzrtpcpp/ZrtpConfigure.h> + +/** + * The extern references to the global data. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ +extern char clientId[]; +extern char zrtpVersion[]; + +/** + * + */ +extern char HelloMsg[]; +extern char HelloAckMsg[]; +extern char CommitMsg[]; +extern char DHPart1Msg[]; +extern char DHPart2Msg[]; +extern char Confirm1Msg[]; +extern char Confirm2Msg[]; +extern char Conf2AckMsg[]; +extern char ErrorMsg[]; +extern char ErrorAckMsg[]; +extern char GoClearMsg[]; +extern char ClearAckMsg[]; +extern char PingMsg[]; +extern char PingAckMsg[]; +extern char SasRelayMsg[]; +extern char RelayAckMsg[]; + +/** + * + */ +extern char responder[]; +extern char initiator[]; +extern char iniMasterKey[]; +extern char iniMasterSalt[]; +extern char respMasterKey[]; +extern char respMasterSalt[]; + +extern char iniHmacKey[]; +extern char respHmacKey[]; +extern char retainedSec[]; + +extern char iniZrtpKey[]; +extern char respZrtpKey[]; + +extern char sasString[]; + +extern char KDFString[]; +extern char zrtpSessionKey[]; +extern char zrtpMsk[]; +extern char zrtpTrustedMitm[]; + + +extern char s256[]; +extern char s384[]; +extern const char* mandatoryHash; + +extern char aes3[]; +extern char aes2[]; +extern char aes1[]; +extern char two3[]; +extern char two2[]; +extern char two1[]; + +extern const char* mandatoryCipher; + +extern char dh2k[]; +extern char dh3k[]; +extern char ec25[]; +extern char ec38[]; + +extern char mult[]; + +extern const char* mandatoryPubKey; + +extern char b32[]; +extern const char* mandatorySasType; + +extern char hs32[]; +extern char hs80[]; +extern char sk32[]; +extern char sk64[]; +extern const char* mandatoryAuthLen_1; +extern const char* mandatoryAuthLen_2; + +/** + * @} + */ +#endif // _ZRTPTEXTDATA_H_ + diff --git a/src/libzrtpcpp/ZrtpUserCallback.h b/src/libzrtpcpp/ZrtpUserCallback.h new file mode 100644 index 0000000..3e4871c --- /dev/null +++ b/src/libzrtpcpp/ZrtpUserCallback.h @@ -0,0 +1,234 @@ +/*
+ Copyright (C) 2006-2008 Werner Dittmann
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _ZRTPUSERCALLBACK_H_
+#define _ZRTPUSERCALLBACK_H_
+
+/**
+ * @file ZrtpUserCallback.h
+ * @brief The ZRTP UserCallback class
+ *
+ * @ingroup GNU_ZRTP
+ * @{
+ */
+
+#include <stdint.h>
+#include <string>
+
+#include <libzrtpcpp/ZrtpCodes.h>
+
+/**
+ * Application callback methods.
+ *
+ * The ccRTP specific part of GNU ZRTP uses these callback methods
+ * to report ZRTP events to the application. This class implements a
+ * default behaviour for each callback method, usually just a return.
+ *
+ * An application may extend this class and overload methods
+ * to implement its own behaviour. The application must register its
+ * callback class using ZrtpQueue#setUserCallback().
+ *
+ * <b>CAVEAT</b><br/>
+ * All methods of the user callback class and classes that
+ * extend this class run in the context of the RTP thread. Thus it is
+ * of paramount importance to keep the execution time of the methods
+ * as short as possible.
+ *
+ * @author Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+
+class __EXPORT ZrtpUserCallback {
+
+ public:
+
+ /// Create the stadard user callback class.
+ ZrtpUserCallback() {}
+
+ virtual ~ZrtpUserCallback() {};
+
+ /**
+ * Inform user interface that security is active now.
+ *
+ * ZRTP calls this method if the sender and the receiver are
+ * in secure mode now.
+ *
+ * @param cipher
+ * Name and mode of cipher used to encrypt the SRTP stream
+ */
+ virtual void secureOn(std::string cipher) {
+ return;
+ }
+ /**
+ * Inform user interface that security is not active any more.
+ *
+ * ZRTP calls this method if either the sender or the receiver
+ * left secure mode.
+ *
+ */
+ virtual void secureOff() {
+ return;
+ }
+
+ /**
+ * Show the Short Authentication String (SAS) on user interface.
+ *
+ * ZRTP calls this method to display the SAS and inform about the SAS
+ * verification status. The user interface shall enable a SAS verfication
+ * button (or similar UI element). The user shall click on this UI
+ * element after he/she confirmed the SAS code with the partner.
+ *
+ * @param sas
+ * The string containing the SAS.
+ * @param verified
+ * If <code>verified</code> is true then SAS was verified by both
+ * parties during a previous call, otherwise it is set to false.
+ */
+ virtual void showSAS(std::string sas, bool verified) {
+ return;
+ }
+
+ /**
+ * Inform the user that ZRTP received "go clear" message from its peer.
+ *
+ * On receipt of a go clear message the user is requested to confirm
+ * a switch to unsecure (clear) modus. Until the user confirms ZRTP
+ * (and the underlying RTP) does not send any data.
+ */
+ virtual void confirmGoClear() {
+ return;
+ }
+
+ /**
+ * Show some information to user.
+ *
+ * ZRTP calls this method to display some information to the user.
+ * Along with the message ZRTP provides a severity indicator that
+ * defines: Info, Warning, Error, and Alert. Refer to the <code>
+ * MessageSeverity</code> enum in <code>ZrtpCodes.h</code>. The
+ * UI may use this indicator to highlight messages or alike.
+ *
+ * @param sev
+ * Severity of the message.
+ * @param subCode
+ * The subcode identifying the reason.
+ */
+ virtual void showMessage(GnuZrtpCodes::MessageSeverity sev, int32_t subCode) {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this if the negotiation failed.
+ *
+ * ZRTPQueue calls this method in case ZRTP negotiation failed. The
+ * parameters show the severity as well as some explanatory text.
+ * Refer to the <code>MessageSeverity</code> enum above.
+ *
+ * @param severity
+ * This defines the message's severity
+ * @param subCode
+ * The subcode identifying the reason.
+ */
+ virtual void zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity,
+ int32_t subCode) {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this method if the other side does not support ZRTP.
+ *
+ * If the other side does not answer the ZRTP <em>Hello</em> packets then
+ * ZRTP calls this method.
+ *
+ */
+ virtual void zrtpNotSuppOther() {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this method to inform about a PBX enrollment request.
+ *
+ * Please refer to chapter 8.3 ff to get more details about PBX enrollment
+ * and SAS relay.
+ *
+ * @param info
+ * Give some information to the user about the PBX requesting an
+ * enrollment.
+ *
+ */
+ virtual void zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this method to inform about PBX enrollment result.
+ *
+ * Informs the use about the acceptance or denial of an PBX enrollment
+ * request
+ *
+ * @param info
+ * Give some information to the user about the result of an
+ * enrollment.
+ *
+ */
+ virtual void zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this method to request a SAS signature.
+ *
+ * After ZRTP core was able to compute the Short Authentication String
+ * (SAS) it calls this method. The client may now use an approriate
+ * method to sign the SAS. The client may use
+ * setSignatureData() of ZrtpQueue to store the signature
+ * data an enable signature transmission to the other peer. Refer
+ * to chapter 8.2 of ZRTP specification.
+ *
+ * @param sasHash
+ * Pointer to the 32 byte SAS hash to be signed.
+ * @see ZrtpQueue#setSignatureData
+ *
+ */
+ virtual void signSAS(uint8_t* sasHash) {
+ return;
+ }
+
+ /**
+ * ZRTPQueue calls this method to request a SAS signature check.
+ *
+ * After ZRTP received a SAS signature in one of the Confirm packets it
+ * call this method. The client may use <code>getSignatureLength()</code>
+ * and <code>getSignatureData()</code>of ZrtpQueue to get the signature
+ * data and perform the signature check. Refer to chapter 8.2 of ZRTP
+ * specification.
+ *
+ * If the signature check fails the client may return false to ZRTP. In
+ * this case ZRTP signals an error to the other peer and terminates
+ * the ZRTP handshake.
+ *
+ * @param sasHash
+ * Pointer to the 32 byte SAS hash that was signed by the other peer.
+ * @return
+ * true if the signature was ok, false otherwise.
+ *
+ */
+ virtual bool checkSASSignature(uint8_t* sasHash) {
+ return true;
+ }
+};
+
+#endif
diff --git a/src/libzrtpcpp/crypto/TwoCFB.cpp b/src/libzrtpcpp/crypto/TwoCFB.cpp new file mode 100755 index 0000000..be3dda4 --- /dev/null +++ b/src/libzrtpcpp/crypto/TwoCFB.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2011 by Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** Copyright (C) 2011 + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <string.h> + +#include <libzrtpcpp/crypto/twoCFB.h> +#include <libzrtpcpp/crypto/twofish.h> + +static int initialized = 0; + +void twoCfbEncrypt(uint8_t* key, int32_t keyLength, uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + Twofish_key keyCtx; + int usedBytes = 0; + + if (!initialized) { + Twofish_initialise(); + initialized = 1; + } + + memset(&keyCtx, 0, sizeof(Twofish_key)); + Twofish_prepare_key(key, keyLength, &keyCtx); + + Twofish_cfb128_encrypt(&keyCtx, (Twofish_Byte*)data, (Twofish_Byte*)data, + (size_t)dataLength, (Twofish_Byte*)IV, &usedBytes); +} + + +void twoCfbDecrypt(uint8_t* key, int32_t keyLength, const uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + Twofish_key keyCtx; + int usedBytes = 0; + + if (!initialized) { + Twofish_initialise(); + initialized = 1; + } + + memset(&keyCtx, 0, sizeof(Twofish_key)); + Twofish_prepare_key(key, keyLength, &keyCtx); + + Twofish_cfb128_decrypt(&keyCtx, (Twofish_Byte*)data, (Twofish_Byte*)data, + (size_t)dataLength, (Twofish_Byte*)IV, &usedBytes); +} diff --git a/src/libzrtpcpp/crypto/ZrtpDH.h b/src/libzrtpcpp/crypto/ZrtpDH.h new file mode 100644 index 0000000..bd32f7f --- /dev/null +++ b/src/libzrtpcpp/crypto/ZrtpDH.h @@ -0,0 +1,168 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _ZRTPDH_H__ +#define _ZRTPDH_H__ + +#include <stdint.h> +#include <libzrtpcpp/ZrtpConfigure.h> + +/** + * @file ZrtpDH.h + * @brief Class that implemets Diffie-Helman key agreement for ZRTP + * + * @ingroup GNU_ZRTP + * @{ + */ + +/** + * Generates a number of random bytes. + * + * @param buf + * Pointer to a buffer that receives the random data. Must have a size + * of at least <code>length</code> bytes. + * + * @param length + * Number of random bytes to produce. + */ +void randomZRTP(uint8_t *buf, int32_t length); + +const int32_t DH2K = 0; +const int32_t DH3K = 1; +const int32_t EC25 = 2; +const int32_t EC38 = 3; + + +/** + * Implementation of Diffie-Helman for ZRTP + * + * This class defines functions to generate and compute the + * Diffie-Helman public and secret data and the shared secret. According to + * the ZRTP specification we use the MODP groups as defined by RFC 3526 for + * length 3072 and 4096. + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class ZrtpDH { + +private: + void* ctx; ///< Context the DH + int pkType; ///< Which type of DH to use + +public: + /** + * Create a Diffie-Helman key agreement algorithm + * + * @param type + * Name of the DH algorithm to use + */ + ZrtpDH(const char* type); + + ~ZrtpDH(); + + /** + * Generates a public key based on the DH parameters and a random + * private key. + * + * @return 1 on success, 0 on failure + */ + int32_t generatePublicKey(); + + /** + * Returns the size in bytes of the DH parameter p. + * + * @return Size in bytes. + */ + int32_t getDhSize() const; + + /** + * Returns the size in bytes of computed public key. + * + * @return Size in bytes. + */ + int32_t getPubKeySize() const; + + /** + * Returns the bytes of computed secret key. + * + * Returns the bytes of the public key in network (big endian) order.# + * + * @param buf + * Pointer to a buffer of at least <code>getPubKeySize()</code> bytes. + * + * @return Size in bytes. + */ + int32_t getPubKeyBytes(uint8_t *buf) const; + + /** + * Compute the secret key and returns it to caller. + * + * This method computes the secret key based on the DH parameters, the + * private key and the peer's public key. + * + * @param pubKeyBytes + * Pointer to the peer's public key bytes. Must be in big endian order. + * + * @param secret + * Pointer to a buffer that receives the secret key. This buffer must + * have a length of at least <code>getSecretSize()</code> bytes. + * + * @return the size of the shared secret on success, -1 on error. + */ + int32_t computeSecretKey(uint8_t *pubKeyBytes, uint8_t *secret); + + /** + * Check and validate the public key received from peer. + * + * Check if this is a correct Diffie-Helman public key. If the public + * key value is either one or (P-1) then this is a wrong public key + * value. + * + * @param pubKeyBytes + * Pointer to the peer's public key bytes. Must be in big endian order. + * + * @return 0 if check faild, 1 if public key value is ok. + */ + int32_t checkPubKey(uint8_t* pubKeyBytes) const; + + /** + * Get type of DH algorithm. + * + * @return + * Pointer to DH algorithm name + */ + const char* getDHtype(); +}; + +#endif // ZRTPDH_H + +/** + * @} + */ + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/crypto/aesCFB.h b/src/libzrtpcpp/crypto/aesCFB.h new file mode 100644 index 0000000..6b4ab6e --- /dev/null +++ b/src/libzrtpcpp/crypto/aesCFB.h @@ -0,0 +1,87 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _AESCFB_H__ +#define _AESCFB_H__ + +#include <stdint.h> + +/** + * @file aesCFB.h + * @brief Function that provide AES CFB mode support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#ifndef AES_BLOCK_SIZE +#define AES_BLOCK_SIZE 16 +#endif + +/** + * Encrypt data with AES CFB mode, full block feedback size. + * + * This functions takes one data chunk and encrypts it with + * AES CFB mode. The lenght of the data may be arbitrary and + * it is not needed to be a multiple of AES blocksize. + * + * @param key + * Points to the key bytes. + * @param keyLength + * Length of the key in bytes + * @param IV + * The initialization vector which must be AES_BLOCKSIZE (16) bytes. + * @param data + * Points to a buffer that contains and receives the computed + * the data (in-place encryption). + * @param dataLength + * Length of the data in bytes + */ + +void aesCfbEncrypt(uint8_t* key, int32_t keyLength, uint8_t* IV, uint8_t *data, + int32_t dataLength); + +/** + * Decrypt data with AES CFB mode, full block feedback size. + * + * This functions takes one data chunk and decrypts it with + * AES CFB mode. The lenght of the data may be arbitrary and + * it is not needed to be a multiple of AES blocksize. + * + * @param key + * Points to the key bytes. + * @param keyLength + * Length of the key in bytes + * @param IV + * The initialization vector which must be AES_BLOCKSIZE (16) bytes. + * @param data + * Points to a buffer that contains and receives the computed + * the data (in-place decryption). + * @param dataLength + * Length of the data in bytes + */ + +void aesCfbDecrypt(uint8_t* key, int32_t keyLength, const uint8_t* IV, uint8_t *data, + int32_t dataLength); +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/crypto/gcrypt/InitializeGcrypt.cpp b/src/libzrtpcpp/crypto/gcrypt/InitializeGcrypt.cpp new file mode 100644 index 0000000..1372578 --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/InitializeGcrypt.cpp @@ -0,0 +1,130 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> + +#include <malloc.h> +#include <errno.h> +#include <gcrypt.h> + +#include <libzrtpcpp-config.h> +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#else +#include <winbase.h> +#endif + +static int initialized = 0; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_PTHREAD_H +static int gcry_thread_mutex_init (void **priv) +{ + int err = 0; + pthread_mutex_t *lock = (pthread_mutex_t *)malloc (sizeof (pthread_mutex_t)); + if (!lock) + err = ENOMEM; + if (!err) { + err = pthread_mutex_init (lock, NULL); + if (err) + free (lock); + else + *priv = lock; + } + return err; +} + +static int gcry_thread_mutex_destroy (void **lock) +{ + int err = pthread_mutex_destroy ((pthread_mutex_t *)*lock); + free (*lock); return err; +} + +static int gcry_thread_mutex_lock (void **lock) +{ + return pthread_mutex_lock ((pthread_mutex_t *)*lock); +} + +static int gcry_thread_mutex_unlock (void **lock) +{ + return pthread_mutex_unlock ((pthread_mutex_t *)*lock); +} + +static struct gcry_thread_cbs gcry_threads = { + GCRY_THREAD_OPTION_PTHREAD, NULL, + gcry_thread_mutex_init, gcry_thread_mutex_destroy, + gcry_thread_mutex_lock, gcry_thread_mutex_unlock +}; + +#else +static int gcry_thread_mutex_init (void **priv) +{ + int err = 0; + CRITICAL_SECTION *lock = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION)); + if (!lock) + err = ENOMEM; + if (!err) { + InitializeCriticalSection(lock); + *priv = lock; + } + return err; +} + +static int gcry_thread_mutex_destroy (void **lock) +{ + free(*lock); + return 0; +} + +static int gcry_thread_mutex_lock (void **lock) +{ + EnterCriticalSection((CRITICAL_SECTION *)*lock); + return 0; +} + +static int gcry_thread_mutex_unlock (void **lock) +{ + LeaveCriticalSection((CRITICAL_SECTION *)*lock); + return 0; +} + +static struct gcry_thread_cbs gcry_threads = { + GCRY_THREAD_OPTION_PTHREAD, NULL, + gcry_thread_mutex_init, gcry_thread_mutex_destroy, + gcry_thread_mutex_lock, gcry_thread_mutex_unlock +}; +#endif + +#ifdef __cplusplus +} +#endif + +int initializeGcrypt () +{ + + if (initialized) { + return 1; + } + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads); + gcry_check_version(NULL); + gcry_control(GCRYCTL_DISABLE_SECMEM); + initialized = 1; + return 1; +} diff --git a/src/libzrtpcpp/crypto/gcrypt/gcryptAesCFB.cpp b/src/libzrtpcpp/crypto/gcrypt/gcryptAesCFB.cpp new file mode 100644 index 0000000..3673619 --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcryptAesCFB.cpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** Copyright (C) 2006, 2007 + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/aesCFB.h> + + +extern void initializeGcrypt(); + +void aesCfbEncrypt(uint8_t* key, int32_t keyLength, uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + gcry_error_t err = 0; + int algo; + + initializeGcrypt(); + + if (keyLength == 16) { + algo = GCRY_CIPHER_AES; + } + else if (keyLength == 32) { + algo = GCRY_CIPHER_AES256; + } + else { + return; + } + gcry_cipher_hd_t tmp; + err = gcry_cipher_open(&tmp, algo, GCRY_CIPHER_MODE_CFB, 0); + err = gcry_cipher_setkey(tmp, key, keyLength); + err = gcry_cipher_setiv (tmp, IV, AES_BLOCK_SIZE); + err = gcry_cipher_encrypt (tmp, data, dataLength, data, dataLength); + gcry_cipher_close(tmp); +} + +void aesCfbDecrypt(uint8_t* key, int32_t keyLength, const uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + gcry_error_t err = 0; + int algo; + + initializeGcrypt(); + + if (keyLength == 16) { + algo = GCRY_CIPHER_AES; + } + else if (keyLength == 32) { + algo = GCRY_CIPHER_AES256; + } + else { + return; + } + gcry_cipher_hd_t tmp; + err = gcry_cipher_open(&tmp, algo, GCRY_CIPHER_MODE_CFB, 0); + err = gcry_cipher_setkey(tmp, key, keyLength); + err = gcry_cipher_setiv (tmp, IV, AES_BLOCK_SIZE); + err = gcry_cipher_decrypt (tmp, data, dataLength, data, dataLength); + gcry_cipher_close(tmp); +} diff --git a/src/libzrtpcpp/crypto/gcrypt/gcryptZrtpDH.cpp b/src/libzrtpcpp/crypto/gcrypt/gcryptZrtpDH.cpp new file mode 100644 index 0000000..1aba680 --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcryptZrtpDH.cpp @@ -0,0 +1,349 @@ +/* + Copyright (C) 2006, 2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** Copyright (C) 2006, 2009 + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/ZrtpDH.h> +#include <libzrtpcpp/ZrtpTextData.h> +#include <sstream> + +struct gcryptCtx { + gcry_mpi_t privKey; + gcry_mpi_t pubKey; +// int32_t pLength; +}; + +extern void initializeGcrypt(); + +static gcry_mpi_t bnP2048 = NULL; +static gcry_mpi_t bnP3072 = NULL; +// static gcry_mpi_t bnP4096 = NULL; +static gcry_mpi_t two = NULL; +static gcry_mpi_t bnP2048MinusOne = NULL; +static gcry_mpi_t bnP3072MinusOne = NULL; +// static gcry_mpi_t bnP4096MinusOne = NULL; + +static uint8_t dhinit = 0; + +void randomZRTP(uint8_t *buf, int32_t length) { + initializeGcrypt(); + gcry_randomize(buf, length, GCRY_STRONG_RANDOM); +} + +static const uint8_t P2048[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t P3072[] = + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, + 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, + 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, + 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, + 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, + 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, + 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + /* ************* +static const uint8_t P4096[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, + 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, + 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, + 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, + 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, + 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, + 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, + 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, + 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, + 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, + 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, + 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + *************** */ +#define DH3K 1 +#define DH2K 0 +ZrtpDH::ZrtpDH(const char* type){ + + // Well - the algo type is only 4 char thus cast to int32 and compare + if (*(int32_t*)type == *(int32_t*)dh2k) { + pkType = DH2K; + } + else if (*(int32_t*)type == *(int32_t*)dh3k) { + pkType = DH3K; + } + else { + fprintf(stderr, "Unknown pubkey algo: %d\n", pkType); + } + ctx = static_cast<void*>(new gcryptCtx); + gcryptCtx* tmpCtx = static_cast<gcryptCtx*>(ctx); + tmpCtx->privKey = NULL; + tmpCtx->pubKey = NULL; + + initializeGcrypt(); + + if (!dhinit) { + gcry_mpi_scan(&bnP2048, GCRYMPI_FMT_USG, P2048, sizeof(P2048), NULL); + gcry_mpi_scan(&bnP3072, GCRYMPI_FMT_USG, P3072, sizeof(P3072), NULL); +// gcry_mpi_scan(&bnP4096, GCRYMPI_FMT_USG, P4096, sizeof(P4096), NULL); + two = gcry_mpi_set_ui(NULL, 2); + + bnP2048MinusOne = gcry_mpi_new(sizeof(P2048)*8); + gcry_mpi_sub_ui(bnP2048MinusOne, bnP2048, 1); + + bnP3072MinusOne = gcry_mpi_new(sizeof(P3072)*8); + gcry_mpi_sub_ui(bnP3072MinusOne, bnP3072, 1); + +// bnP4096MinusOne = gcry_mpi_new(sizeof(P4096)*8); +// gcry_mpi_sub_ui(bnP4096MinusOne, bnP4096, 1); + dhinit = 1; + } + + if (pkType == DH3K) { + tmpCtx->privKey = gcry_mpi_new(256); + gcry_mpi_randomize(tmpCtx->privKey, 256, GCRY_STRONG_RANDOM); + } + else if (pkType == DH2K) { + tmpCtx->privKey = gcry_mpi_new(512); + gcry_mpi_randomize(tmpCtx->privKey, 512, GCRY_STRONG_RANDOM); + } +// else { +// tmpCtx->privKey = gcry_mpi_new(512); +// gcry_mpi_randomize(tmpCtx->privKey, 512, GCRY_STRONG_RANDOM); +// } +} + +ZrtpDH::~ZrtpDH() { + gcryptCtx* tmpCtx = static_cast<gcryptCtx*>(ctx); + + if (tmpCtx != NULL) { + gcry_mpi_release(tmpCtx->privKey); + tmpCtx->privKey = NULL; + gcry_mpi_release(tmpCtx->pubKey); + tmpCtx->pubKey = NULL; + delete tmpCtx; + ctx = NULL; + } +} + +int32_t ZrtpDH::computeSecretKey(uint8_t *pubKeyBytes, uint8_t *secret) { + + int32_t length = getDhSize(); + gcryptCtx* tmpCtx = static_cast<gcryptCtx*>(ctx); + + gcry_mpi_t pubKeyOther; + gcry_mpi_t sec = gcry_mpi_new(0); + gcry_mpi_scan(&pubKeyOther, GCRYMPI_FMT_USG, pubKeyBytes, length, NULL); + + if (pkType == DH2K) { + gcry_mpi_powm(sec, pubKeyOther, tmpCtx->privKey, bnP2048); + } + else if (pkType == DH3K) { + gcry_mpi_powm(sec, pubKeyOther, tmpCtx->privKey, bnP3072); + } + else { +// gcry_mpi_powm(sec, pubKeyOther, tmpCtx->privKey, bnP4096); + return 0; + } + gcry_mpi_release(pubKeyOther); + + size_t result; + gcry_mpi_print(GCRYMPI_FMT_USG, secret, length, &result, sec); + gcry_mpi_release(sec); + + return result; +} + +int32_t ZrtpDH::generatePublicKey() +{ + gcryptCtx* tmpCtx = static_cast<gcryptCtx*>(ctx); + + tmpCtx->pubKey = gcry_mpi_new(0); + if (pkType == DH2K) { + gcry_mpi_powm(tmpCtx->pubKey, two, tmpCtx->privKey, bnP2048); + } + else if (pkType == DH3K) { + gcry_mpi_powm(tmpCtx->pubKey, two, tmpCtx->privKey, bnP3072); + } + else { +// gcry_mpi_powm(tmpCtx->pubKey, two, tmpCtx->privKey, bnP4096); + return 0; + } + return 1; +} + +int32_t ZrtpDH::getPubKeyBytes(uint8_t *buf) const +{ + gcryptCtx* tmpCtx = static_cast<gcryptCtx*>(ctx); + int32_t len = getPubKeySize(); + + // get length of Dh in bytes, prepend buffer with zeros if necessary + int32_t prepend = getDhSize() - getPubKeySize(); + if (prepend > 0) { + memset(buf, 0, prepend); + } + size_t i = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, buf + prepend, len, &i, tmpCtx->pubKey); + return i; +} + +int32_t ZrtpDH::getDhSize() const +{ + switch (pkType) { + case DH2K: + return 2048/8; + break; + case DH3K: + return 3072/8; + break; + } + return 0; +} + +int32_t ZrtpDH::getPubKeySize() const +{ + return ((gcry_mpi_get_nbits(static_cast<gcryptCtx*>(ctx)->pubKey) + 7) / 8); +} + +int32_t ZrtpDH::checkPubKey(uint8_t *pubKeyBytes) const +{ + gcry_mpi_t pubKeyOther = NULL; + gcry_mpi_scan(&pubKeyOther, GCRYMPI_FMT_USG, pubKeyBytes, getDhSize(), NULL); + + if (pkType == DH2K) { + if (gcry_mpi_cmp(bnP2048MinusOne, pubKeyOther) == 0) + return 0; + } + else if (pkType == DH3K) { + if (gcry_mpi_cmp(bnP3072MinusOne, pubKeyOther) == 0) + return 0; + } + else { +// if (gcry_mpi_cmp(bnP4096MinusOne, pubKeyOther) == 0) + return 0; + } + if (gcry_mpi_cmp_ui(pubKeyOther, 1) == 0) { + return 0; + } + + gcry_mpi_release(pubKeyOther); + return 1; +} + +const char* ZrtpDH::getDHtype() +{ + switch (pkType) { + case DH2K: + return dh2k; + break; + case DH3K: + return dh3k; + break; + } + return NULL; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/crypto/gcrypt/gcrypthmac256.cpp b/src/libzrtpcpp/crypto/gcrypt/gcrypthmac256.cpp new file mode 100644 index 0000000..3a44504 --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcrypthmac256.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/hmac256.h> + +void hmac_sha256(uint8_t* key, uint32_t keyLength, + uint8_t* data, int32_t dataLength, + uint8_t* mac, uint32_t* macLength) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + gcry_md_write (hd, data, dataLength); + + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA256); + memcpy(mac, p, SHA256_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA256_DIGEST_LENGTH; + } + gcry_md_close (hd); +} + +void hmac_sha256( uint8_t* key, uint32_t keyLength, + uint8_t* dataChunks[], + uint32_t dataChunkLength[], + uint8_t* mac, uint32_t* macLength ) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA256); + memcpy(mac, p, SHA256_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA256_DIGEST_LENGTH; + } + gcry_md_close (hd); +} diff --git a/src/libzrtpcpp/crypto/gcrypt/gcrypthmac384.cpp b/src/libzrtpcpp/crypto/gcrypt/gcrypthmac384.cpp new file mode 100644 index 0000000..c48813c --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcrypthmac384.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2006-2009 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/hmac384.h> + +void hmac_sha384(uint8_t* key, uint32_t keyLength, + uint8_t* data, int32_t dataLength, + uint8_t* mac, uint32_t* macLength) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA384, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + gcry_md_write (hd, data, dataLength); + + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA384); + memcpy(mac, p, SHA384_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA384_DIGEST_LENGTH; + } + gcry_md_close (hd); +} + +void hmac_sha384( uint8_t* key, uint32_t keyLength, + uint8_t* dataChunks[], + uint32_t dataChunkLength[], + uint8_t* mac, uint32_t* macLength ) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA384, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA384); + memcpy(mac, p, SHA384_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA384_DIGEST_LENGTH; + } + gcry_md_close (hd); +} diff --git a/src/libzrtpcpp/crypto/gcrypt/gcryptsha256.cpp b/src/libzrtpcpp/crypto/gcrypt/gcryptsha256.cpp new file mode 100644 index 0000000..0c32bd8 --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcryptsha256.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/sha256.h> + +void sha256(unsigned char* data, unsigned int dataLength, + unsigned char* mac) +{ + gcry_md_hash_buffer(GCRY_MD_SHA256, mac, data, dataLength); +} + +void sha256(unsigned char* dataChunks[], + unsigned int dataChunkLength[], + unsigned char* mac) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA256, 0); + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA256); + memcpy(mac, p, SHA256_DIGEST_LENGTH); + gcry_md_close (hd); +} + +void* createSha256Context() +{ + gcry_error_t err = 0; + gcry_md_hd_t hd; + + err = gcry_md_open(&hd, GCRY_MD_SHA256, 0); + return (void*)hd; +} + +void closeSha256Context(void* ctx, unsigned char* digest) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + if (digest != NULL) { + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA256); + memcpy(digest, p, SHA256_DIGEST_LENGTH); + } + gcry_md_close (hd); +} + +void sha256Ctx(void* ctx, unsigned char* data, + unsigned int dataLength) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + gcry_md_write (hd, data, dataLength); +} + +void sha256Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } +} diff --git a/src/libzrtpcpp/crypto/gcrypt/gcryptsha384.cpp b/src/libzrtpcpp/crypto/gcrypt/gcryptsha384.cpp new file mode 100644 index 0000000..19c8c5b --- /dev/null +++ b/src/libzrtpcpp/crypto/gcrypt/gcryptsha384.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <gcrypt.h> +#include <libzrtpcpp/crypto/sha384.h> + +void sha384(unsigned char* data, unsigned int dataLength, + unsigned char* mac) +{ + gcry_md_hash_buffer(GCRY_MD_SHA384, mac, data, dataLength); +} + +void sha384(unsigned char* dataChunks[], + unsigned int dataChunkLength[], + unsigned char* mac) +{ + gcry_md_hd_t hd; + gcry_error_t err = 0; + + err = gcry_md_open(&hd, GCRY_MD_SHA384, 0); + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA384); + memcpy(mac, p, SHA384_DIGEST_LENGTH); + gcry_md_close (hd); +} + +void* createSha384Context() +{ + gcry_error_t err = 0; + gcry_md_hd_t hd; + + err = gcry_md_open(&hd, GCRY_MD_SHA384, 0); + return (void*)hd; +} + +void closeSha384Context(void* ctx, unsigned char* digest) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + if (digest != NULL) { + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA384); + memcpy(digest, p, SHA384_DIGEST_LENGTH); + } + gcry_md_close (hd); +} + +void sha384Ctx(void* ctx, unsigned char* data, + unsigned int dataLength) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + gcry_md_write (hd, data, dataLength); +} + +void sha384Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]) +{ + gcry_md_hd_t hd = (gcry_md_hd_t)ctx; + + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } +} diff --git a/src/libzrtpcpp/crypto/hmac256.h b/src/libzrtpcpp/crypto/hmac256.h new file mode 100644 index 0000000..f9110a6 --- /dev/null +++ b/src/libzrtpcpp/crypto/hmac256.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 2006, 2005, 2004 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/** + * Methods to compute a SHA256 HMAC. + * + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef HMAC_SHA256_H +#define HMAC_SHA256_H + +/** + * @file hmac256.h + * @brief Function that provide SHA256 HMAC support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdint.h> + +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + +/** + * Compute SHA256 HMAC. + * + * This functions takes one data chunk and computes its SHA256 HMAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 32 bytes (SHA256_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ +void hmac_sha256( uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length ); + +/** + * Compute SHA256 HMAC over several data cunks. + * + * This functions takes several data chunk and computes the SHA256 HAMAC. It + * uses the openSSL HAMAC SHA256 implementation. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 32 bytes (SHA256_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ + +void hmac_sha256( uint8_t* key, uint32_t key_length, + uint8_t* data[], uint32_t data_length[], + uint8_t* mac, uint32_t* mac_length ); +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/crypto/hmac384.h b/src/libzrtpcpp/crypto/hmac384.h new file mode 100644 index 0000000..223444a --- /dev/null +++ b/src/libzrtpcpp/crypto/hmac384.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 2009, 2006, 2005, 2004 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/** + * Methods to compute a SHA384 HMAC. + * + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef HMAC_SHA384_H +#define HMAC_SHA384_H + +/** + * @file hmac384.h + * @brief Function that provide SHA384 HMAC support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdint.h> + +#ifndef SHA384_DIGEST_LENGTH +#define SHA384_DIGEST_LENGTH 48 +#endif + +/** + * Compute SHA384 HMAC. + * + * This functions takes one data chunk and computes its SHA384 HMAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 48 bytes (SHA384_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ +void hmac_sha384( uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length ); + +/** + * Compute SHA384 HMAC over several data cunks. + * + * This functions takes several data chunk and computes the SHA384 HAMAC. It + * uses the openSSL HAMAC SHA384 implementation. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 48 bytes (SHA384_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ + +void hmac_sha384( uint8_t* key, uint32_t key_length, + uint8_t* data[], uint32_t data_length[], + uint8_t* mac, uint32_t* mac_length ); +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/crypto/openssl/AesCFB.cpp b/src/libzrtpcpp/crypto/openssl/AesCFB.cpp new file mode 100644 index 0000000..595d1ff --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/AesCFB.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2006, 2007 by Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** Copyright (C) 2006, 2007 + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <openssl/crypto.h> +#include <openssl/aes.h> +#include <string.h> + +#include <libzrtpcpp/crypto/aesCFB.h> + +// extern void initializeOpenSSL(); + + +void aesCfbEncrypt(uint8_t* key, int32_t keyLength, uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + AES_KEY aesKey; + int usedBytes = 0; + +// initializeOpenSSL(); + + memset(&aesKey, 0, sizeof( AES_KEY ) ); + if (keyLength == 16) { + AES_set_encrypt_key(key, 128, &aesKey); + } + else if (keyLength == 32) { + AES_set_encrypt_key(key, 256, &aesKey); + } + else { + return; + } + AES_cfb128_encrypt(data, data, dataLength, &aesKey, + IV, &usedBytes, AES_ENCRYPT); +} + + +void aesCfbDecrypt(uint8_t* key, int32_t keyLength, const uint8_t* IV, uint8_t *data, + int32_t dataLength) +{ + AES_KEY aesKey; + int usedBytes = 0; + +// initializeOpenSSL(); + + memset(&aesKey, 0, sizeof( AES_KEY ) ); + if (keyLength == 16) { + AES_set_encrypt_key(key, 128, &aesKey); + } + else if (keyLength == 32) { + AES_set_encrypt_key(key, 256, &aesKey); + } + else { + return; + } + AES_cfb128_encrypt(data, data, dataLength, &aesKey, + (unsigned char*)IV, &usedBytes, AES_DECRYPT); +} diff --git a/src/libzrtpcpp/crypto/openssl/InitializeOpenSSL.cpp b/src/libzrtpcpp/crypto/openssl/InitializeOpenSSL.cpp new file mode 100755 index 0000000..17961c3 --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/InitializeOpenSSL.cpp @@ -0,0 +1,242 @@ +/* + Copyright (C) 2006 Werner Dittmann + + 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, 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., 59 Temple Place, Boston, MA 02111. +*/ + +#include <stdio.h> +#include <openssl/evp.h> +#include <libzrtpcpp-config.h> + +#if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) +#undef _MSWINDOWS_ +#define _MSWINDOWS_ +#include <windows.h> +#endif + +#if defined SOLARIS && !defined HAVE_PTHREAD_H +#include <synch.h> +#include <thread.h> +#endif +#if !defined(_MSWINDOWS_) && !defined SOLARIS +#include <pthread.h> +#endif + +#ifdef const +#undef const +#endif + +static void threadLockSetup(void); +static void threadLockCleanup(void); +static void myLockingCallback(int, int, const char *, int); + +/** + * Implement the locking callback functions for openSSL. + * + * Unfortunatly we can't use the Commonc++ Mutex here because the + * Mutex may use (for some cases) the Commonc++ Thread class. OpenSSL + * does not use this Thread class. + */ + +static int initialized = 0; + +int initializeOpenSSL () +{ + + if (initialized) { + return 1; + } + initialized = 1; + threadLockSetup(); + return 1; +} + +int finalizeOpenSSL () +{ + if(!initialized) + return 1; + + initialized = 0; + threadLockCleanup(); + return 1; +} + +#ifdef _MSWINDOWS_ +#define __LOCKING + +static HANDLE *lock_cs; + +static void threadLockSetup(void) { + int i; + + lock_cs=(HANDLE*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(HANDLE)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + lock_cs[i] = CreateMutex(NULL,FALSE,NULL); + } + + CRYPTO_set_locking_callback((void (*)(int,int,const char *,int))myLockingCallback); + /* id callback defined */ +} + +static void threadLockCleanup(void) { + int i; + + CRYPTO_set_locking_callback(NULL); + for (i = 0; i < CRYPTO_num_locks(); i++) { + CloseHandle(lock_cs[i]); + } + OPENSSL_free(lock_cs); +} + +static void myLockingCallback(int mode, int type, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + WaitForSingleObject(lock_cs[type], INFINITE); + } + else { + ReleaseMutex(lock_cs[type]); + } +} + +#endif /* OPENSSL_SYS_WIN32 */ + + +#if defined SOLARIS && !defined HAVE_PTHREAD_H && !defined(_MSWINDOWS) +#define __LOCKING + +static mutex_t *lock_cs; +static long *lock_count; + +static void threadLockSetup(void) { + int i; + + lock_cs = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(mutex_t)); + lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + lock_count[i] = 0; + /* rwlock_init(&(lock_cs[i]),USYNC_THREAD,NULL); */ + mutex_init(&(lock_cs[i]), USYNC_THREAD, NULL); + } + CRYPTO_set_locking_callback((void (*)(int, int ,const char *, int))myLockingCallback); +} + +static void threadLockCleanup(void) { + int i; + + CRYPTO_set_locking_callback(NULL); + + fprintf(stderr,"cleanup\n"); + + for (i = 0; i < CRYPTO_num_locks(); i++) { + /* rwlock_destroy(&(lock_cs[i])); */ + mutex_destroy(&(lock_cs[i])); + fprintf(stderr,"%8ld:%s\n",lock_count[i],CRYPTO_get_lock_name(i)); + } + OPENSSL_free(lock_cs); + OPENSSL_free(lock_count); +} + +static void myLockingCallback(int mode, int type, const char *file, int line) +{ +#ifdef undef + fprintf(stderr,"thread=%4d mode=%s lock=%s %s:%d\n", + CRYPTO_thread_id(), + (mode&CRYPTO_LOCK)?"l":"u", + (type&CRYPTO_READ)?"r":"w",file,line); +#endif + + /* + if (CRYPTO_LOCK_SSL_CERT == type) + fprintf(stderr,"(t,m,f,l) %ld %d %s %d\n", + CRYPTO_thread_id(), + mode,file,line); + */ + if (mode & CRYPTO_LOCK) { + mutex_lock(&(lock_cs[type])); + lock_count[type]++; + } + else { + mutex_unlock(&(lock_cs[type])); + } +} + +static unsigned long solaris_thread_id(void) { + unsigned long ret; + + ret=(unsigned long)thr_self(); + return(ret); +} +#endif /* SOLARIS */ + + +#ifndef __LOCKING +static pthread_mutex_t* lock_cs; +static long* lock_count; + +static void threadLockSetup(void) { + int i; + + lock_cs = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); + lock_count = (long*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + lock_count[i] = 0; + pthread_mutex_init(&(lock_cs[i]),NULL); + } + + // CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id); + CRYPTO_set_locking_callback((void (*)(int,int,const char *, int))myLockingCallback); +} + +static void threadLockCleanup(void) +{ + int i; + + CRYPTO_set_locking_callback(NULL); + fprintf(stderr,"cleanup\n"); + for (i = 0; i < CRYPTO_num_locks(); i++) { + pthread_mutex_destroy(&(lock_cs[i])); + fprintf(stderr,"%8ld:%s\n",lock_count[i], + CRYPTO_get_lock_name(i)); + } + OPENSSL_free(lock_cs); + OPENSSL_free(lock_count); +} + +static void myLockingCallback(int mode, int type, const char *file, + int line) { +#ifdef undef + fprintf(stderr,"thread=%4d mode=%s lock=%s %s:%d\n", + CRYPTO_thread_id(), + (mode&CRYPTO_LOCK)?"l":"u", + (type&CRYPTO_READ)?"r":"w",file,line); +#endif + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(&(lock_cs[type])); + lock_count[type]++; + } + else { + pthread_mutex_unlock(&(lock_cs[type])); + } +} + +/* +static unsigned long pthreads_thread_id(void) +{ + unsigned long ret; + + ret = (unsigned long)pthread_self(); + return(ret); +} +*/ +#endif diff --git a/src/libzrtpcpp/crypto/openssl/ZrtpDH.cpp b/src/libzrtpcpp/crypto/openssl/ZrtpDH.cpp new file mode 100644 index 0000000..fbd623c --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/ZrtpDH.cpp @@ -0,0 +1,426 @@ +/* + Copyright (C) 2006, 2009 by Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** Copyright (C) 2006, 2009 + * + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <string.h> + +#include <openssl/crypto.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <openssl/dh.h> +#include <openssl/evp.h> +#include <openssl/ec.h> +#include <openssl/ecdh.h> + +#include <libzrtpcpp/crypto/ZrtpDH.h> +#include <libzrtpcpp/ZrtpTextData.h> + +// extern void initializeOpenSSL(); + +static BIGNUM* bnP2048 = NULL; +static BIGNUM* bnP3072 = NULL; +// static BIGNUM* bnP4096 = NULL; + +static BIGNUM* bnP2048MinusOne = NULL; +static BIGNUM* bnP3072MinusOne = NULL; +// static BIGNUM* bnP4096MinusOne = NULL; + +static uint8_t dhinit = 0; + +void randomZRTP(uint8_t *buf, int32_t length) +{ +// initializeOpenSSL(); + RAND_bytes(buf, length); +} + +static const uint8_t P2048[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const uint8_t P3072[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, + 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, + 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, + 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, + 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, + 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, + 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* ************** +static const uint8_t P4096[] = +{ +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, +0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, +0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, +0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, +0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, +0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, +0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, +0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, +0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, +0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, +0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, +0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, +0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, +0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, +0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, +0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, +0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, +0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, +0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, +0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, +0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, +0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, +0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, +0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, +0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0, +0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, +0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, +0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, +0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0, +0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, +0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, +0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, +0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18, +0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, +0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB, +0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, +0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F, +0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, +0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76, +0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, +0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC, +0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +*************** */ + +ZrtpDH::ZrtpDH(const char* type) { + + uint8_t random[64]; + + // Well - the algo type is only 4 char thus cast to int32 and compare + if (*(int32_t*)type == *(int32_t*)dh2k) { + pkType = DH2K; + } + else if (*(int32_t*)type == *(int32_t*)dh3k) { + pkType = DH3K; + } + else if (*(int32_t*)type == *(int32_t*)ec25) { + pkType = EC25; + } + else if (*(int32_t*)type == *(int32_t*)ec38) { + pkType = EC38; + } + else { + return; + } + +// initializeOpenSSL(); + + if (!dhinit) { + bnP2048 = BN_bin2bn(P2048,sizeof(P2048),NULL); + bnP3072 = BN_bin2bn(P3072,sizeof(P3072),NULL); +// bnP4096 = BN_bin2bn(P4096,sizeof(P4096),NULL); + + bnP2048MinusOne = BN_dup(bnP2048); + BN_sub_word(bnP2048MinusOne, 1); + + bnP3072MinusOne = BN_dup(bnP3072); + BN_sub_word(bnP3072MinusOne, 1); + +// bnP4096MinusOne = BN_dup(bnP4096); +// BN_sub_word(bnP4096MinusOne, 1); + dhinit = 1; + } + + DH* tmpCtx = NULL; + switch (pkType) { + case DH2K: + case DH3K: + ctx = static_cast<void*>(DH_new()); + tmpCtx = static_cast<DH*>(ctx); + tmpCtx->g = BN_new(); + BN_set_word(tmpCtx->g, DH_GENERATOR_2); + + if (pkType == DH2K) { + tmpCtx->p = BN_dup(bnP2048); + RAND_bytes(random, 32); + tmpCtx->priv_key = BN_bin2bn(random, 32, NULL); + } + else if (pkType == DH3K) { + tmpCtx->p = BN_dup(bnP3072); + RAND_bytes(random, 64); + tmpCtx->priv_key = BN_bin2bn(random, 32, NULL); + } + break; + + case EC25: + ctx = static_cast<void*>(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + break; + case EC38: + ctx = static_cast<void*>(EC_KEY_new_by_curve_name(NID_secp384r1)); + break; + } +} + +ZrtpDH::~ZrtpDH() { + if (ctx == NULL) + return; + + switch (pkType) { + case DH2K: + case DH3K: + DH_free(static_cast<DH*>(ctx)); + break; + + case EC25: + case EC38: + EC_KEY_free(static_cast<EC_KEY*>(ctx)); + break; + } +} + +int32_t ZrtpDH::computeSecretKey(uint8_t *pubKeyBytes, uint8_t *secret) { + + if (pkType == DH2K || pkType == DH3K) { + DH* tmpCtx = static_cast<DH*>(ctx); + + if (tmpCtx->pub_key != NULL) { + BN_free(tmpCtx->pub_key); + } + tmpCtx->pub_key = BN_bin2bn(pubKeyBytes, getDhSize(), NULL); + return DH_compute_key(secret, tmpCtx->pub_key, tmpCtx); + } + if (pkType == EC25 || pkType == EC38) { + uint8_t buffer[100]; + int32_t ret; + int32_t len = getPubKeySize(); + + buffer[0] = POINT_CONVERSION_UNCOMPRESSED; + memcpy(buffer+1, pubKeyBytes, len); + + EC_POINT* point = EC_POINT_new(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx))); + EC_POINT_oct2point(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx)), + point, buffer, len+1, NULL); + ret = ECDH_compute_key(secret, getDhSize(), point, static_cast<EC_KEY*>(ctx), NULL); + EC_POINT_free(point); + return ret; + } + return -1; +} + +int32_t ZrtpDH::generatePublicKey() +{ + if (pkType == DH2K || pkType == DH3K) + return DH_generate_key(static_cast<DH*>(ctx)); + + if (pkType == EC25 || pkType == EC38) + return EC_KEY_generate_key(static_cast<EC_KEY*>(ctx)); + return 0; +} + +int32_t ZrtpDH::getDhSize() const +{ + if (pkType == DH2K || pkType == DH3K) + return DH_size(static_cast<DH*>(ctx)); + + if (pkType == EC25) + return 32; + if (pkType == EC38) + return 48; + + return 0; +} + +int32_t ZrtpDH::getPubKeySize() const +{ + if (pkType == DH2K || pkType == DH3K) + return BN_num_bytes(static_cast<DH*>(ctx)->pub_key); + + if (pkType == EC25 || pkType == EC38) + return EC_POINT_point2oct(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx)), + EC_KEY_get0_public_key(static_cast<EC_KEY*>(ctx)), + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL) - 1; + return 0; + +} + +int32_t ZrtpDH::getPubKeyBytes(uint8_t *buf) const +{ + + if (pkType == DH2K || pkType == DH3K) { + // get len of pub_key, prepend with zeros to DH size + int32_t prepend = getDhSize() - getPubKeySize(); + if (prepend > 0) { + memset(buf, 0, prepend); + } + return BN_bn2bin(static_cast<DH*>(ctx)->pub_key, buf + prepend); + } + if (pkType == EC25 || pkType == EC38) { + uint8_t buffer[100]; + + int len = EC_POINT_point2oct(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx)), + EC_KEY_get0_public_key(static_cast<EC_KEY*>(ctx)), + POINT_CONVERSION_UNCOMPRESSED, buffer, 100, NULL); + memcpy(buf, buffer+1, len-1); + return len-1; + } + return 0; +} + +int32_t ZrtpDH::checkPubKey(uint8_t *pubKeyBytes) const +{ + if (pkType == EC25 || pkType == EC38) { + uint8_t buffer[100]; + int32_t ret; + int32_t len = getPubKeySize(); + + buffer[0] = POINT_CONVERSION_UNCOMPRESSED; + memcpy(buffer+1, pubKeyBytes, len); + + EC_POINT* point = EC_POINT_new(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx))); + EC_POINT_oct2point(EC_KEY_get0_group(static_cast<EC_KEY*>(ctx)), + point, buffer, len+1, NULL); + EC_KEY* chkKey = EC_KEY_new(); + EC_KEY_set_group(chkKey, EC_KEY_get0_group(static_cast<EC_KEY*>(ctx))); + EC_KEY_set_public_key(chkKey, point); + ret = EC_KEY_check_key(chkKey); + + EC_POINT_free(point); + EC_KEY_free(chkKey); + + return ret; + } + + BIGNUM* pubKeyOther = BN_bin2bn(pubKeyBytes, getDhSize(), NULL); + + if (pkType == DH2K) { + if (BN_cmp(bnP2048MinusOne, pubKeyOther) == 0) + return 0; + } + else if (pkType == DH3K) { + if (BN_cmp(bnP3072MinusOne, pubKeyOther) == 0) + return 0; + } + else { +// if (BN_cmp(bnP4096MinusOne, pubKeyOther) == 0) + return 0; + } + int one = BN_is_one(pubKeyOther); + if (one == 1) + return 0; + + BN_free(pubKeyOther); + return 1; +} + +const char* ZrtpDH::getDHtype() +{ + switch (pkType) { + case DH2K: + return dh2k; + break; + case DH3K: + return dh3k; + break; + case EC25: + return ec25; + break; + case EC38: + return ec38; + break; + } + return NULL; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/crypto/openssl/hmac256.cpp b/src/libzrtpcpp/crypto/openssl/hmac256.cpp new file mode 100644 index 0000000..a054c02 --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/hmac256.cpp @@ -0,0 +1,67 @@ +/* + Copyright (C) 2005, 2004 Erik Eliasson, Johan Bilien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + +*/ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + */ + +#include <openssl/hmac.h> +#include <libzrtpcpp/crypto/hmac256.h> + +void hmac_sha256(uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length) +{ + unsigned int tmp; + HMAC( EVP_sha256(), key, key_length, data, data_length, mac, &tmp ); + *mac_length = tmp; +} + +void hmac_sha256(uint8_t* key, uint32_t key_length, + uint8_t* data_chunks[], + uint32_t data_chunck_length[], + uint8_t* mac, uint32_t* mac_length ) +{ + unsigned int tmp; + HMAC_CTX ctx; + HMAC_CTX_init( &ctx ); + HMAC_Init_ex( &ctx, key, key_length, EVP_sha256(), NULL ); + while( *data_chunks ){ + HMAC_Update( &ctx, *data_chunks, *data_chunck_length ); + data_chunks ++; + data_chunck_length ++; + } + HMAC_Final( &ctx, mac, &tmp); + *mac_length = tmp; + HMAC_CTX_cleanup( &ctx ); +} diff --git a/src/libzrtpcpp/crypto/openssl/hmac384.cpp b/src/libzrtpcpp/crypto/openssl/hmac384.cpp new file mode 100644 index 0000000..10d6fbc --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/hmac384.cpp @@ -0,0 +1,67 @@ +/* + Copyright (C) 2005, 2004 Erik Eliasson, Johan Bilien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + +*/ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + */ + +#include <openssl/hmac.h> +#include <libzrtpcpp/crypto/hmac256.h> + +void hmac_sha384(uint8_t* key, uint32_t key_length, + uint8_t* data, int32_t data_length, + uint8_t* mac, uint32_t* mac_length) +{ + unsigned int tmp; + HMAC( EVP_sha384(), key, key_length, data, data_length, mac, &tmp ); + *mac_length = tmp; +} + +void hmac_sha384(uint8_t* key, uint32_t key_length, + uint8_t* data_chunks[], + uint32_t data_chunck_length[], + uint8_t* mac, uint32_t* mac_length ) +{ + unsigned int tmp; + HMAC_CTX ctx; + HMAC_CTX_init( &ctx ); + HMAC_Init_ex( &ctx, key, key_length, EVP_sha384(), NULL ); + while( *data_chunks ){ + HMAC_Update( &ctx, *data_chunks, *data_chunck_length ); + data_chunks ++; + data_chunck_length ++; + } + HMAC_Final( &ctx, mac, &tmp); + *mac_length = tmp; + HMAC_CTX_cleanup( &ctx ); +} diff --git a/src/libzrtpcpp/crypto/openssl/sha256.cpp b/src/libzrtpcpp/crypto/openssl/sha256.cpp new file mode 100644 index 0000000..2163a6d --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/sha256.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2005, 2004 Erik Eliasson, Johan Bilien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <openssl/crypto.h> +#include <openssl/sha.h> + +#include <libzrtpcpp/crypto/sha256.h> + +void sha256(unsigned char *data, unsigned int data_length, + unsigned char *digest ) +{ + SHA256(data, data_length, digest); +} + +void sha256(unsigned char * data_chunks[], + unsigned int data_chunck_length[], + unsigned char *digest) +{ + SHA256_CTX ctx; + SHA256_Init( &ctx); + while(*data_chunks) { + SHA256_Update(&ctx, *data_chunks, *data_chunck_length); + data_chunks++; + data_chunck_length++; + } + SHA256_Final(digest, &ctx); +} + +void* createSha256Context() +{ + SHA256_CTX* ctx = (SHA256_CTX*)malloc(sizeof (SHA256_CTX)); + SHA256_Init(ctx); + return (void*)ctx; +} + +void closeSha256Context(void* ctx, unsigned char* digest) +{ + SHA256_CTX* hd = (SHA256_CTX*)ctx; + + if (digest != NULL) { + SHA256_Final(digest, hd); + } + free(hd); +} + +void sha256Ctx(void* ctx, unsigned char* data, + unsigned int dataLength) +{ + SHA256_CTX* hd = (SHA256_CTX*)ctx; + SHA256_Update(hd, data, dataLength); +} + +void sha256Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]) +{ + SHA256_CTX* hd = (SHA256_CTX*)ctx; + + while (*dataChunks) { + SHA256_Update (hd, *dataChunks, *dataChunkLength); + dataChunks++; + dataChunkLength++; + } +} diff --git a/src/libzrtpcpp/crypto/openssl/sha384.cpp b/src/libzrtpcpp/crypto/openssl/sha384.cpp new file mode 100644 index 0000000..9d166e7 --- /dev/null +++ b/src/libzrtpcpp/crypto/openssl/sha384.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2005, 2004 Erik Eliasson, Johan Bilien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <openssl/crypto.h> +#include <openssl/sha.h> + +#include <libzrtpcpp/crypto/sha384.h> + +void sha384(unsigned char *data, unsigned int data_length, + unsigned char *digest ) +{ + SHA384(data, data_length, digest); +} + +void sha384(unsigned char * data_chunks[], + unsigned int data_chunck_length[], + unsigned char *digest) +{ + SHA512_CTX ctx; + SHA384_Init( &ctx); + while(*data_chunks) { + SHA384_Update(&ctx, *data_chunks, *data_chunck_length); + data_chunks++; + data_chunck_length++; + } + SHA384_Final(digest, &ctx); +} + +void* createSha384Context() +{ + SHA512_CTX* ctx = (SHA512_CTX*)malloc(sizeof (SHA512_CTX)); + SHA384_Init(ctx); + return (void*)ctx; +} + +void closeSha384Context(void* ctx, unsigned char* digest) +{ + SHA512_CTX* hd = (SHA512_CTX*)ctx; + + if (digest != NULL) { + SHA384_Final(digest, hd); + } + free(hd); +} + +void sha384Ctx(void* ctx, unsigned char* data, + unsigned int dataLength) +{ + SHA512_CTX* hd = (SHA512_CTX*)ctx; + SHA384_Update(hd, data, dataLength); +} + +void sha384Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]) +{ + SHA512_CTX* hd = (SHA512_CTX*)ctx; + + while (*dataChunks) { + SHA384_Update (hd, *dataChunks, *dataChunkLength); + dataChunks++; + dataChunkLength++; + } +} diff --git a/src/libzrtpcpp/crypto/sha256.h b/src/libzrtpcpp/crypto/sha256.h new file mode 100644 index 0000000..959a620 --- /dev/null +++ b/src/libzrtpcpp/crypto/sha256.h @@ -0,0 +1,144 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * Functions to compute SHA256 digest. + * + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _SHA256_H +#define _SHA256_H + +/** + * @file sha256.h + * @brief Function that provide SHA256 support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdint.h> + +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + +/** + * Compute SHA256 digest. + * + * This functions takes one data chunk and computes its SHA256 digest. This + * function creates and deletes an own SHA256 context to perform the SHA256 + * operations. + * + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param digest + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 32 bytes (SHA256_DIGEST_LENGTH). + */ +void sha256(unsigned char *data, + unsigned int data_length, + unsigned char *digest); + +/** + * Compute SHA256 digest over several data cunks. + * + * This functions takes several data chunks and computes the SHA256 digest. + * This function creates and deletes an own SHA256 context to perform the + * SHA256 operations. + * + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param digest + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 32 bytes (SHA256_DIGEST_LENGTH). + */ +void sha256(unsigned char *data[], + unsigned int data_length[], + unsigned char *digest); +/** + * Create and initialize a SHA256 context. + * + * An application uses this context to hash several data into one SHA256 + * digest. See also sha256Ctx(...) and closeSha256Context(...). + * + * @return Returns a pointer to the initialized SHA256 context + */ +void* createSha256Context(); + +/** + * Compute a digest and close the SHa256 digest. + * + * An application uses this function to compute the SHA256 digest and to + * close the SHA256 context. + * + * @param ctx + * Points to the SHA256 context. + * @param digest + * If this pointer is not NULL then it must point to a byte array that + * is big enough to hold the SHA256 digest (256 bit = 32 Bytes). If this + * pointer is NULL then the functions does not compute the digest but + * closes the context only. The context cannot be used anymore. + */ +void closeSha256Context(void* ctx, + unsigned char* digest); + +/** + * Update the SHA256 context with data. + * + * This functions updates the SHA256 context with some data. + * See also CloseSha256Context(...) how to get the digest. + * + * @param ctx + * Points to the SHA256 context. + * @param data + * Points to the data to update the context. + * @param dataLength + * The length of the data in bytes. + */ +void sha256Ctx(void* ctx, unsigned char* data, + unsigned int dataLength); + +/** + * Update the SHA256 context with several data chunks. + * + * This functions updates the SHA256 context with some data. + * See also CloseSha256Context(...) how to get the digest. + * + * @param ctx + * Points to the SHA256 context. + * @param dataChunks + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param dataChunkLength + * Points to an array of integers that hold the length of each data chunk. + * + */ +void sha256Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]); + +/** + * @} + */ +#endif + diff --git a/src/libzrtpcpp/crypto/sha384.h b/src/libzrtpcpp/crypto/sha384.h new file mode 100644 index 0000000..6cb7a70 --- /dev/null +++ b/src/libzrtpcpp/crypto/sha384.h @@ -0,0 +1,144 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * Functions to compute SHA384 digest. + * + * @author: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _SHA384_H +#define _SHA384_H + +/** + * @file sha384.h + * @brief Function that provide SHA384 support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdint.h> + +#ifndef SHA384_DIGEST_LENGTH +#define SHA384_DIGEST_LENGTH 48 +#endif + +/** + * Compute SHA384 digest. + * + * This functions takes one data chunk and computes its SHA384 digest. This + * function creates and deletes an own SHA384 context to perform the SHA384 + * operations. + * + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param digest + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 48 bytes (SHA384_DIGEST_LENGTH). + */ +void sha384(unsigned char *data, + unsigned int data_length, + unsigned char *digest); + +/** + * Compute SHA384 digest over several data cunks. + * + * This functions takes several data chunks and computes the SHA384 digest. + * This function creates and deletes an own SHA384 context to perform the + * SHA384 operations. + * + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param digest + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 48 bytes (SHA384_DIGEST_LENGTH). + */ +void sha384(unsigned char *data[], + unsigned int data_length[], + unsigned char *digest); +/** + * Create and initialize a SHA384 context. + * + * An application uses this context to hash several data into one SHA384 + * digest. See also sha384Ctx(...) and closeSha384Context(...). + * + * @return Returns a pointer to the initialized SHA384 context + */ +void* createSha384Context(); + +/** + * Compute a digest and close the SHA384 digest. + * + * An application uses this function to compute the SHA384 digest and to + * close the SHA384 context. + * + * @param ctx + * Points to the SHA384 context. + * @param digest + * If this pointer is not NULL then it must point to a byte array that + * is big enough to hold the SHA384 digest (384 bit = 48 Bytes). If this + * pointer is NULL then the functions does not compute the digest but + * closes the context only. The context cannot be used anymore. + */ +void closeSha384Context(void* ctx, + unsigned char* digest); + +/** + * Update the SHA384 context with data. + * + * This functions updates the SHA384 context with some data. + * See also CloseSha384Context(...) how to get the digest. + * + * @param ctx + * Points to the SHA384 context. + * @param data + * Points to the data to update the context. + * @param dataLength + * The length of the data in bytes. + */ +void sha384Ctx(void* ctx, unsigned char* data, + unsigned int dataLength); + +/** + * Update the SHA384 context with several data chunks. + * + * This functions updates the SHA384 context with some data. + * See also CloseSha384Context(...) how to get the digest. + * + * @param ctx + * Points to the SHA384 context. + * @param dataChunks + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param dataChunkLength + * Points to an array of integers that hold the length of each data chunk. + * + */ +void sha384Ctx(void* ctx, unsigned char* dataChunks[], + unsigned int dataChunkLength[]); + +/** + * @} + */ +#endif + diff --git a/src/libzrtpcpp/crypto/twoCFB.h b/src/libzrtpcpp/crypto/twoCFB.h new file mode 100755 index 0000000..593a59c --- /dev/null +++ b/src/libzrtpcpp/crypto/twoCFB.h @@ -0,0 +1,87 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef _TWOCFB_H__ +#define _TWOCFB_H__ + +#include <stdint.h> + +/** + * @file aesCFB.h + * @brief Function that provide AES CFB mode support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#ifndef TWO_BLOCK_SIZE +#define TWO_BLOCK_SIZE 16 +#endif + +/** + * Encrypt data with Twofish CFB mode, full block feedback size. + * + * This functions takes one data chunk and encrypts it with + * Twofish CFB mode. The lenght of the data may be arbitrary and + * it is not needed to be a multiple of Twofish blocksize. + * + * @param key + * Points to the key bytes. + * @param keyLength + * Length of the key in bytes + * @param IV + * The initialization vector which must be TWO_BLOCKSIZE (16) bytes. + * @param data + * Points to a buffer that contains and receives the computed + * the data (in-place encryption). + * @param dataLength + * Length of the data in bytes + */ + +void twoCfbEncrypt(uint8_t* key, int32_t keyLength, uint8_t* IV, uint8_t *data, + int32_t dataLength); + +/** + * Decrypt data with Twofish CFB mode, full block feedback size. + * + * This functions takes one data chunk and decrypts it with + * Twofish CFB mode. The lenght of the data may be arbitrary and + * it is not needed to be a multiple of Twofish blocksize. + * + * @param key + * Points to the key bytes. + * @param keyLength + * Length of the key in bytes + * @param IV + * The initialization vector which must be TWO_BLOCKSIZE (16) bytes. + * @param data + * Points to a buffer that contains and receives the computed + * the data (in-place decryption). + * @param dataLength + * Length of the data in bytes + */ + +void twoCfbDecrypt(uint8_t* key, int32_t keyLength, const uint8_t* IV, uint8_t *data, + int32_t dataLength); +/** + * @} + */ +#endif diff --git a/src/libzrtpcpp/crypto/twofish.c b/src/libzrtpcpp/crypto/twofish.c new file mode 100644 index 0000000..3d390ea --- /dev/null +++ b/src/libzrtpcpp/crypto/twofish.c @@ -0,0 +1,1733 @@ +/*
+ * Fast, portable, and easy-to-use Twofish implementation,
+ * Version 0.3.
+ * Copyright (c) 2002 by Niels Ferguson.
+ * (See further down for the almost-unrestricted licensing terms.)
+ *
+ * --------------------------------------------------------------------------
+ * There are two files for this implementation:
+ * - twofish.h, the header file.
+ * - twofish.c, the code file.
+ *
+ * To incorporate this code into your program you should:
+ * - Check the licensing terms further down in this comment.
+ * - Fix the two type definitions in twofish.h to suit your platform.
+ * - Fix a few definitions in twofish.c in the section marked
+ * PLATFORM FIXES. There is one important ones that affects
+ * functionality, and then a few definitions that you can optimise
+ * for efficiency but those have no effect on the functionality.
+ * Don't change anything else.
+ * - Put the code in your project and compile it.
+ *
+ * To use this library you should:
+ * - Call Twofish_initialise() in your program before any other function in
+ * this library.
+ * - Use Twofish_prepare_key(...) to convert a key to internal form.
+ * - Use Twofish_encrypt(...) and Twofish_decrypt(...) to encrypt and decrypt
+ * data.
+ * See the comments in the header file for details on these functions.
+ * --------------------------------------------------------------------------
+ *
+ * There are many Twofish implementation available for free on the web.
+ * Most of them are hard to integrate into your own program.
+ * As we like people to use our cipher, I thought I would make it easier.
+ * Here is a free and easy-to-integrate Twofish implementation in C.
+ * The latest version is always available from my personal home page at
+ * http://niels.ferguson.net/
+ *
+ * Integrating library code into a project is difficult because the library
+ * header files interfere with the project's header files and code.
+ * And of course the project's header files interfere with the library code.
+ * I've tried to resolve these problems here.
+ * The header file of this implementation is very light-weight.
+ * It contains two typedefs, a structure, and a few function declarations.
+ * All names it defines start with "Twofish_".
+ * The header file is therefore unlikely to cause problems in your project.
+ * The code file of this implementation doesn't need to include the header
+ * files of the project. There is thus no danger of the project interfering
+ * with all the definitions and macros of the Twofish code.
+ * In most situations, all you need to do is fill in a few platform-specific
+ * definitions in the header file and code file,
+ * and you should be able to run the Twofish code in your project.
+ * I estimate it should take you less than an hour to integrate this code
+ * into your project, most of it spent reading the comments telling you what
+ * to do.
+ *
+ * For people using C++: it is very easy to wrap this library into a
+ * TwofishKey class. One of the big advantages is that you can automate the
+ * wiping of the key material in the destructor. I have not provided a C++
+ * class because the interface depends too much on the abstract base class
+ * you use for block ciphers in your program, which I don't know about.
+ *
+ * This implementation is designed for use on PC-class machines. It uses the
+ * Twofish 'full' keying option which uses large tables. Total table size is
+ * around 5-6 kB for static tables plus 4.5 kB for each pre-processed key.
+ * If you need an implementation that uses less memory,
+ * take a look at Brian Gladman's code on his web site:
+ * http://fp.gladman.plus.com/cryptography_technology/aes/
+ * He has code for all AES candidates.
+ * His Twofish code has lots of options trading off table size vs. speed.
+ * You can also take a look at the optimised code by Doug Whiting on the
+ * Twofish web site
+ * http://www.counterpane.com/twofish.html
+ * which has loads of options.
+ * I believe these existing implementations are harder to re-use because they
+ * are not clean libraries and they impose requirements on the environment.
+ * This implementation is very careful to minimise those,
+ * and should be easier to integrate into any larger program.
+ *
+ * The default mode of this implementation is fully portable as it uses no
+ * behaviour not defined in the C standard. (This is harder than you think.)
+ * If you have any problems porting the default mode, please let me know
+ * so that I can fix the problem. (But only if this code is at fault, I
+ * don't fix compilers.)
+ * Most of the platform fixes are related to non-portable but faster ways
+ * of implementing certain functions.
+ *
+ * In general I've tried to make the code as fast as possible, at the expense
+ * of memory and code size. However, C does impose limits, and this
+ * implementation will be slower than an optimised assembler implementation.
+ * But beware of assembler implementations: a good Pentium implementation
+ * uses completely different code than a good Pentium II implementation.
+ * You basically have to re-write the assembly code for every generation of
+ * processor. Unless you are severely pressed for speed, stick with C.
+ *
+ * The initialisation routine of this implementation contains a self-test.
+ * If initialisation succeeds without calling the fatal routine, then
+ * the implementation works. I don't think you can break the implementation
+ * in such a way that it still passes the tests, unless you are malicious.
+ * In other words: if the initialisation routine returns,
+ * you have successfully ported the implementation.
+ * (Or not implemented the fatal routine properly, but that is your problem.)
+ *
+ * I'm indebted to many people who helped me in one way or another to write
+ * this code. During the design of Twofish and the AES process I had very
+ * extensive discussions of all implementation issues with various people.
+ * Doug Whiting in particular provided a wealth of information. The Twofish
+ * team spent untold hours discussion various cipher features, and their
+ * implementation. Brian Gladman implemented all AES candidates in C,
+ * and we had some fruitful discussions on how to implement Twofish in C.
+ * Jan Nieuwenhuizen tested this code on Linux using GCC.
+ *
+ * Now for the license:
+ * The author hereby grants a perpetual license to everybody to
+ * use this code for any purpose as long as the copyright message is included
+ * in the source code of this or any derived work.
+ *
+ * Yes, this means that you, your company, your club, and anyone else
+ * can use this code anywhere you want. You can change it and distribute it
+ * under the GPL, include it in your commercial product without releasing
+ * the source code, put it on the web, etc.
+ * The only thing you cannot do is remove my copyright message,
+ * or distribute any source code based on this implementation that does not
+ * include my copyright message.
+ *
+ * I appreciate a mention in the documentation or credits,
+ * but I understand if that is difficult to do.
+ * I also appreciate it if you tell me where and why you used my code.
+ *
+ * Please send any questions or comments to niels@ferguson.net
+ *
+ * Have Fun!
+ *
+ * Niels
+ */
+
+/*
+ * DISCLAIMER: As I'm giving away my work for free, I'm of course not going
+ * to accept any liability of any form. This code, or the Twofish cipher,
+ * might very well be flawed; you have been warned.
+ * This software is provided as-is, without any kind of warrenty or
+ * guarantee. And that is really all you can expect when you download
+ * code for free from the Internet.
+ *
+ * I think it is really sad that disclaimers like this seem to be necessary.
+ * If people only had a little bit more common sense, and didn't come
+ * whining like little children every time something happens....
+ */
+
+/*
+ * Version history:
+ * Version 0.0, 2002-08-30
+ * First written.
+ * Version 0.1, 2002-09-03
+ * Added disclaimer. Improved self-tests.
+ * Version 0.2, 2002-09-09
+ * Removed last non-portabilities. Default now works completely within
+ * the C standard. UInt32 can be larger than 32 bits without problems.
+ * Version 0.3, 2002-09-28
+ * Bugfix: use instead of to adhere to ANSI/ISO.
+ * Rename BIG_ENDIAN macro to CPU_IS_BIG_ENDIAN. The gcc library
+ * header already defines BIG_ENDIAN, even though it is not
+ * supposed to.
+ */
+
+
+/*
+ * Minimum set of include files.
+ * You should not need any application-specific include files for this code.
+ * In fact, adding you own header files could break one of the many macros or
+ * functions in this file. Be very careful.
+ * Standard include files will probably be ok.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+/* #include * for memset(), memcpy(), and memcmp() */
+#include "twofish.h"
+
+
+/*
+ * PLATFORM FIXES
+ * ==============
+ *
+ * Fix the type definitions in twofish.h first!
+ *
+ * The following definitions have to be fixed for each particular platform
+ * you work on. If you have a multi-platform program, you no doubt have
+ * portable definitions that you can substitute here without changing the
+ * rest of the code.
+ */
+
+
+/*
+ * Function called if something is fatally wrong with the implementation.
+ * This fatal function is called when a coding error is detected in the
+ * Twofish implementation, or when somebody passes an obviously erroneous
+ * parameter to this implementation. There is not much you can do when
+ * the code contains bugs, so we just stop.
+ *
+ * The argument is a string. Ideally the fatal function prints this string
+ * as an error message. Whatever else this function does, it should never
+ * return. A typical implementation would stop the program completely after
+ * printing the error message.
+ *
+ * This default implementation is not very useful,
+ * but does not assume anything about your environment.
+ * It will at least let you know something is wrong....
+ * I didn't want to include any libraries to print and error or so,
+ * as this makes the code much harder to integrate in a project.
+ *
+ * Note that the Twofish_fatal function may not return to the caller.
+ * Unfortunately this is not something the self-test can test for,
+ * so you have to make sure of this yourself.
+ *
+ * If you want to call an external function, be careful about including
+ * your own header files here. This code uses a lot of macros, and your
+ * header file could easily break it. Maybe the best solution is to use
+ * a separate extern statement for your fatal function.
+ */
+/* #define Twofish_fatal(pmsgx) { fprintf(stderr, pmsgx); exit(1); } */
+#define Twofish_fatal(pmsgx, code) { return(code); }
+
+
+/*
+ * The rest of the settings are not important for the functionality
+ * of this Twofish implementation. That is, their default settings
+ * work on all platforms. You can change them to improve the
+ * speed of the implementation on your platform. Erroneous settings
+ * will result in erroneous implementations, but the self-test should
+ * catch those.
+ */
+
+
+/*
+ * Macros to rotate a Twofish_UInt32 value left or right by the
+ * specified number of bits. This should be a 32-bit rotation,
+ * and not rotation of, say, 64-bit values.
+ *
+ * Every encryption or decryption operation uses 32 of these rotations,
+ * so it is a good idea to make these macros efficient.
+ *
+ * This fully portable definition has one piece of tricky stuff.
+ * The UInt32 might be larger than 32 bits, so we have to mask
+ * any higher bits off. The simplest way to do this is to 'and' the
+ * value first with 0xffffffff and then shift it right. An optimising
+ * compiler that has a 32-bit type can optimise this 'and' away.
+ *
+ * Unfortunately there is no portable way of writing the constant
+ * 0xffffffff. You don't know which suffix to use (U, or UL?)
+ * The UINT32_MASK definition uses a bit of trickery. Shift-left
+ * is only defined if the shift amount is strictly less than the size
+ * of the UInt32, so we can't use (1<<32). The answer it to take the value
+ * 2, cast it to a UInt32, shift it left 31 positions, and subtract one.
+ * Another example of how to make something very simple extremely difficult.
+ * I hate C.
+ *
+ * The rotation macros are straightforward.
+ * They are only applied to UInt32 values, which are _unsigned_
+ * so the >> operator must do a logical shift that brings in zeroes.
+ * On most platforms you will only need to optimise the ROL32 macro; the
+ * ROR32 macro is not inefficient on an optimising compiler as all rotation
+ * amounts in this code are known at compile time.
+ *
+ * On many platforms there is a faster solution.
+ * For example, MS compilers have the __rotl and __rotr functions
+ * that generate x86 rotation instructions.
+ */
+#define UINT32_MASK ( (((Twofish_UInt32)2)<<31) - 1 )
+
+#ifndef _MSC_VER
+#define ROL32(x,n) ( (x)<<(n) | ((x) & UINT32_MASK) >> (32-(n)) )
+#define ROR32(x,n) ( (x)>>(n) | ((x) & UINT32_MASK) << (32-(n)) )
+#else
+#define ROL32(x,n) (_lrotl((x), (n)))
+#define ROR32(x,n) (_lrotr((x), (n)))
+#endif
+
+/*
+ * Select data type for q-table entries.
+ *
+ * Larger entry types cost more memory (1.5 kB), and might be faster
+ * or slower depending on the CPU and compiler details.
+ *
+ * This choice only affects the static data size and the key setup speed.
+ * Functionality, expanded key size, or encryption speed are not affected.
+ * Define to 1 to get large q-table entries.
+ */
+#define LARGE_Q_TABLE 0 /* default = 0 */
+
+
+/*
+ * Method to select a single byte from a UInt32.
+ * WARNING: non-portable code if set; might not work on all platforms.
+ *
+ * Inside the inner loop of Twofish it is necessary to access the 4
+ * individual bytes of a UInt32. This can be done using either shifts
+ * and masks, or memory accesses.
+ *
+ * Set to 0 to use shift and mask operations for the byte selection.
+ * This is more ALU intensive. It is also fully portable.
+ *
+ * Set to 1 to use memory accesses. The UInt32 is stored in memory and
+ * the individual bytes are read from memory one at a time.
+ * This solution is more memory-intensive, and not fully portable.
+ * It might be faster on your platform, or not. If you use this option,
+ * make sure you set the CPU_IS_BIG_ENDIAN flag appropriately.
+ *
+ * This macro does not affect the conversion of the inputs and outputs
+ * of the cipher. See the CONVERT_USING_CASTS macro for that.
+ */
+#define SELECT_BYTE_FROM_UINT32_IN_MEMORY 0 /* default = 0 */
+
+
+/*
+ * Method used to read the input and write the output.
+ * WARNING: non-portable code if set; might not work on all platforms.
+ *
+ * Twofish operates on 32-bit words. The input to the cipher is
+ * a byte array, as is the output. The portable method of doing the
+ * conversion is a bunch of rotate and mask operations, but on many
+ * platforms it can be done faster using a cast.
+ * This only works if your CPU allows UInt32 accesses to arbitrary Byte
+ * addresses.
+ *
+ * Set to 0 to use the shift and mask operations. This is fully
+ * portable. .
+ *
+ * Set to 1 to use a cast. The Byte * is cast to a UInt32 *, and a
+ * UInt32 is read. If necessary (as indicated by the CPU_IS_BIG_ENDIAN
+ * macro) the byte order in the UInt32 is swapped. The reverse is done
+ * to write the output of the encryption/decryption. Make sure you set
+ * the CPU_IS_BIG_ENDIAN flag appropriately.
+ * This option does not work unless a UInt32 is exactly 32 bits.
+ *
+ * This macro only changes the reading/writing of the plaintext/ciphertext.
+ * See the SELECT_BYTE_FROM_UINT32_IN_MEMORY to affect the way in which
+ * a UInt32 is split into 4 bytes for the S-box selection.
+ */
+#define CONVERT_USING_CASTS 0 /* default = 0 */
+
+
+/*
+ * Endianness switch.
+ * Only relevant if SELECT_BYTE_FROM_UINT32_IN_MEMORY or
+ * CONVERT_USING_CASTS is set.
+ *
+ * Set to 1 on a big-endian machine, and to 0 on a little-endian machine.
+ * Twofish uses the little-endian convention (least significant byte first)
+ * and big-endian machines (using most significant byte first)
+ * have to do a few conversions.
+ *
+ * CAUTION: This code has never been tested on a big-endian machine,
+ * because I don't have access to one. Feedback appreciated.
+ */
+#define CPU_IS_BIG_ENDIAN 0
+
+
+/*
+ * Macro to reverse the order of the bytes in a UInt32.
+ * Used to convert to little-endian on big-endian machines.
+ * This macro is always tested, but only used in the encryption and
+ * decryption if CONVERT_USING_CASTS, and CPU_IS_BIG_ENDIAN
+ * are both set. In other words: this macro is only speed-critical if
+ * both these flags have been set.
+ *
+ * This default definition of SWAP works, but on many platforms there is a
+ * more efficient implementation.
+ */
+#define BSWAP(x) ((ROL32((x),8)&0x00ff00ff) | (ROR32((x),8) & 0xff00ff00))
+
+
+/*
+ * END OF PLATFORM FIXES
+ * =====================
+ *
+ * You should not have to touch the rest of this file.
+ */
+
+
+/*
+ * Convert the external type names to some that are easier to use inside
+ * this file. I didn't want to use the names Byte and UInt32 in the
+ * header file, because many programs already define them and using two
+ * conventions at once can be very difficult.
+ * Don't change these definitions! Change the originals
+ * in twofish.h instead.
+ */
+/* A Byte must be an unsigned integer, 8 bits long. */
+/* typedef Twofish_Byte Byte; */
+/* A UInt32 must be an unsigned integer at least 32 bits long. */
+/* typedef Twofish_UInt32 UInt32; */
+
+
+/*
+ * Define a macro ENDIAN_CONVERT.
+ *
+ * We define a macro ENDIAN_CONVERT that performs a BSWAP on big-endian
+ * machines, and is the identity function on little-endian machines.
+ * The code then uses this macro without considering the endianness.
+ */
+
+#if CPU_IS_BIG_ENDIAN
+#define ENDIAN_CONVERT(x) BSWAP(x)
+#else
+#define ENDIAN_CONVERT(x) (x)
+#endif
+
+
+/*
+ * Compute byte offset within a UInt32 stored in memory.
+ *
+ * This is only used when SELECT_BYTE_FROM_UINT32_IN_MEMORY is set.
+ *
+ * The input is the byte number 0..3, 0 for least significant.
+ * Note the use of sizeof() to support UInt32 types that are larger
+ * than 4 bytes.
+ */
+#if CPU_IS_BIG_ENDIAN
+#define BYTE_OFFSET( n ) (sizeof(Twofish_UInt32) - 1 - (n) )
+#else
+#define BYTE_OFFSET( n ) (n)
+#endif
+
+
+/*
+ * Macro to get Byte no. b from UInt32 value X.
+ * We use two different definition, depending on the settings.
+ */
+#if SELECT_BYTE_FROM_UINT32_IN_MEMORY
+ /* Pick the byte from the memory in which X is stored. */
+#define SELECT_BYTE( X, b ) (((Twofish_Byte *)(&(X)))[BYTE_OFFSET(b)])
+#else
+ /* Portable solution: Pick the byte directly from the X value. */
+#define SELECT_BYTE( X, b ) (((X) >> (8*(b))) & 0xff)
+#endif
+
+
+/* Some shorthands because we use byte selection in large formulae. */
+#define b0(X) SELECT_BYTE((X),0)
+#define b1(X) SELECT_BYTE((X),1)
+#define b2(X) SELECT_BYTE((X),2)
+#define b3(X) SELECT_BYTE((X),3)
+
+
+/*
+ * We need macros to load and store UInt32 from/to byte arrays
+ * using the least-significant-byte-first convention.
+ *
+ * GET32( p ) gets a UInt32 in lsb-first form from four bytes pointed to
+ * by p.
+ * PUT32( v, p ) writes the UInt32 value v at address p in lsb-first form.
+ */
+#if CONVERT_USING_CASTS
+
+ /* Get UInt32 from four bytes pointed to by p. */
+#define GET32( p ) ENDIAN_CONVERT( *((Twofish_UInt32 *)(p)) )
+ /* Put UInt32 into four bytes pointed to by p */
+#define PUT32( v, p ) *((Twofish_UInt32 *)(p)) = ENDIAN_CONVERT(v)
+
+#else
+
+ /* Get UInt32 from four bytes pointed to by p. */
+#define GET32( p ) \
+ ( \
+ (Twofish_UInt32)((p)[0]) \
+ | (Twofish_UInt32)((p)[1])<< 8 \
+ | (Twofish_UInt32)((p)[2])<<16 \
+ | (Twofish_UInt32)((p)[3])<<24 \
+ )
+ /* Put UInt32 into four bytes pointed to by p */
+#define PUT32( v, p ) \
+ (p)[0] = (Twofish_Byte)(((v) ) & 0xff); \
+ (p)[1] = (Twofish_Byte)(((v) >> 8) & 0xff); \
+ (p)[2] = (Twofish_Byte)(((v) >> 16) & 0xff); \
+ (p)[3] = (Twofish_Byte)(((v) >> 24) & 0xff)
+
+#endif
+
+
+/*
+ * Test the platform-specific macros.
+ * This function tests the macros defined so far to make sure the
+ * definitions are appropriate for this platform.
+ * If you make any mistake in the platform configuration, this should detect
+ * that and inform you what went wrong.
+ * Somewhere, someday, this is going to save somebody a lot of time,
+ * because misbehaving macros are hard to debug.
+ */
+static int test_platform()
+ {
+ /* Buffer with test values. */
+ Twofish_Byte buf[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0};
+ Twofish_UInt32 C;
+ Twofish_UInt32 x,y;
+ int i;
+
+ /*
+ * Some sanity checks on the types that can't be done in compile time.
+ * A smart compiler will just optimise these tests away.
+ * The pre-processor doesn't understand different types, so we cannot
+ * do these checks in compile-time.
+ *
+ * I hate C.
+ *
+ * The first check in each case is to make sure the size is correct.
+ * The second check is to ensure that it is an unsigned type.
+ */
+ if( ((Twofish_UInt32)((Twofish_UInt32)1 << 31) == 0) || ((Twofish_UInt32)-1 < 0 ))
+ {
+ Twofish_fatal( "Twofish code: Twofish_UInt32 type not suitable", ERR_UINT32 );
+ }
+ if( (sizeof( Twofish_Byte ) != 1) || (((Twofish_Byte)-1) < 0) )
+ {
+ Twofish_fatal( "Twofish code: Twofish_Byte type not suitable", ERR_BYTE );
+ }
+
+ /*
+ * Sanity-check the endianness conversions.
+ * This is just an aid to find problems. If you do the endianness
+ * conversion macros wrong you will fail the full cipher test,
+ * but that does not help you find the error.
+ * Always make it easy to find the bugs!
+ *
+ * Detail: There is no fully portable way of writing UInt32 constants,
+ * as you don't know whether to use the U or UL suffix. Using only U you
+ * might only be allowed 16-bit constants. Using UL you might get 64-bit
+ * constants which cannot be stored in a UInt32 without warnings, and
+ * which generally behave subtly different from a true UInt32.
+ * As long as we're just comparing with the constant,
+ * we can always use the UL suffix and at worst lose some efficiency.
+ * I use a separate '32-bit constant' macro in most of my other code.
+ *
+ * I hate C.
+ *
+ * Start with testing GET32. We test it on all positions modulo 4
+ * to make sure we can handly any position of inputs. (Some CPUs
+ * do not allow non-aligned accesses which we would do if you used
+ * the CONVERT_USING_CASTS option.
+ */
+ if( (GET32( buf ) != 0x78563412UL) || (GET32(buf+1) != 0x9a785634UL)
+ || (GET32( buf+2 ) != 0xbc9a7856UL) || (GET32(buf+3) != 0xdebc9a78UL) )
+ {
+ Twofish_fatal( "Twofish code: GET32 not implemented properly", ERR_GET32 );
+ }
+
+ /*
+ * We can now use GET32 to test PUT32.
+ * We don't test the shifted versions. If GET32 can do that then
+ * so should PUT32.
+ */
+ C = GET32( buf );
+ PUT32( 3*C, buf );
+ if( GET32( buf ) != 0x69029c36UL )
+ {
+ Twofish_fatal( "Twofish code: PUT32 not implemented properly", ERR_PUT32 );
+ }
+
+
+ /* Test ROL and ROR */
+ for( i=1; i<32; i++ )
+ {
+ /* Just a simple test. */
+ x = ROR32( C, i );
+ y = ROL32( C, i );
+ x ^= (C>>i) ^ (C<<(32-i));
+ /*y ^= (C<>(32-i)); */
+ y ^= (C<<i) ^ (C>>(32-i));
+ x |= y;
+ /*
+ * Now all we check is that x is zero in the least significant
+ * 32 bits. Using the UL suffix is safe here, as it doesn't matter
+ * if we get a larger type.
+ */
+ if( (x & 0xffffffffUL) != 0 )
+ {
+ Twofish_fatal( "Twofish ROL or ROR not properly defined.", ERR_ROLR );
+ }
+ }
+
+ /* Test the BSWAP macro */
+ if( BSWAP(C) != 0x12345678UL )
+ {
+ /*
+ * The BSWAP macro should always work, even if you are not using it.
+ * A smart optimising compiler will just remove this entire test.
+ */
+ Twofish_fatal( "BSWAP not properly defined.", ERR_BSWAP );
+ }
+
+ /* And we can test the b macros which use SELECT_BYTE. */
+ if( (b0(C)!=0x12) || (b1(C) != 0x34) || (b2(C) != 0x56) || (b3(C) != 0x78) )
+ {
+ /*
+ * There are many reasons why this could fail.
+ * Most likely is that CPU_IS_BIG_ENDIAN has the wrong value.
+ */
+ Twofish_fatal( "Twofish code: SELECT_BYTE not implemented properly", ERR_SELECTB );
+ }
+ return SUCCESS;
+ }
+
+
+/*
+ * Finally, we can start on the Twofish-related code.
+ * You really need the Twofish specifications to understand this code. The
+ * best source is the Twofish book:
+ * "The Twofish Encryption Algorithm", by Bruce Schneier, John Kelsey,
+ * Doug Whiting, David Wagner, Chris Hall, and Niels Ferguson.
+ * you can also use the AES submission document of Twofish, which is
+ * available from my list of publications on my personal web site at
+ * http://niels.ferguson.net/.
+ *
+ * The first thing we do is write the testing routines. This is what the
+ * implementation has to satisfy in the end. We only test the external
+ * behaviour of the implementation of course.
+ */
+
+
+/*
+ * Perform a single self test on a (plaintext,ciphertext,key) triple.
+ * Arguments:
+ * key array of key bytes
+ * key_len length of key in bytes
+ * p plaintext
+ * c ciphertext
+ */
+static int test_vector( Twofish_Byte key[], int key_len, Twofish_Byte p[16], Twofish_Byte c[16] )
+ {
+ Twofish_Byte tmp[16]; /* scratch pad. */
+ Twofish_key xkey; /* The expanded key */
+ int i;
+
+
+ /* Prepare the key */
+ if ((i = Twofish_prepare_key( key, key_len, &xkey)) < 0)
+ return i;
+
+ /*
+ * We run the test twice to ensure that the xkey structure
+ * is not damaged by the first encryption.
+ * Those are hideous bugs to find if you get them in an application.
+ */
+ for( i=0; i<2; i++ )
+ {
+ /* Encrypt and test */
+ Twofish_encrypt( &xkey, p, tmp );
+ if( memcmp( c, tmp, 16 ) != 0 )
+ {
+ Twofish_fatal( "Twofish encryption failure", ERR_TEST_ENC );
+ }
+
+ /* Decrypt and test */
+ Twofish_decrypt( &xkey, c, tmp );
+ if( memcmp( p, tmp, 16 ) != 0 )
+ {
+ Twofish_fatal( "Twofish decryption failure", ERR_TEST_DEC );
+ }
+ }
+
+ /* The test keys are not secret, so we don't need to wipe xkey. */
+ return SUCCESS;
+ }
+
+
+/*
+ * Check implementation using three (key,plaintext,ciphertext)
+ * test vectors, one for each major key length.
+ *
+ * This is an absolutely minimal self-test.
+ * This routine does not test odd-sized keys.
+ */
+static int test_vectors()
+ {
+ /*
+ * We run three tests, one for each major key length.
+ * These test vectors come from the Twofish specification.
+ * One encryption and one decryption using randomish data and key
+ * will detect almost any error, especially since we generate the
+ * tables ourselves, so we don't have the problem of a single
+ * damaged table entry in the source.
+ */
+
+ /* 128-bit test is the I=3 case of section B.2 of the Twofish book. */
+ static Twofish_Byte k128[] = {
+ 0x9F, 0x58, 0x9F, 0x5C, 0xF6, 0x12, 0x2C, 0x32,
+ 0xB6, 0xBF, 0xEC, 0x2F, 0x2A, 0xE8, 0xC3, 0x5A,
+ };
+ static Twofish_Byte p128[] = {
+ 0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E,
+ 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19
+ };
+ static Twofish_Byte c128[] = {
+ 0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85,
+ 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3
+ };
+
+ /* 192-bit test is the I=4 case of section B.2 of the Twofish book. */
+ static Twofish_Byte k192[] = {
+ 0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36,
+ 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88,
+ 0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44
+ };
+ static Twofish_Byte p192[] = {
+ 0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5,
+ 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2
+ };
+ static Twofish_Byte c192[] = {
+ 0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45,
+ 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65
+ };
+
+ /* 256-bit test is the I=4 case of section B.2 of the Twofish book. */
+ static Twofish_Byte k256[] = {
+ 0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46,
+ 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D,
+ 0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B,
+ 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F
+ };
+ static Twofish_Byte p256[] = {
+ 0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F,
+ 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6
+ };
+ static Twofish_Byte c256[] = {
+ 0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97,
+ 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA
+ };
+
+ int ret;
+
+ /* Run the actual tests. */
+ if ((ret = test_vector( k128, 16, p128, c128 )) < 0)
+ return ret;
+ if ((ret = test_vector( k192, 24, p192, c192 )) < 0)
+ return ret;
+ if ((ret = test_vector( k256, 32, p256, c256 )) < 0)
+ return ret;
+ return SUCCESS;
+ }
+
+
+/*
+ * Perform extensive test for a single key size.
+ *
+ * Test a single key size against the test vectors from section
+ * B.2 in the Twofish book. This is a sequence of 49 encryptions
+ * and decryptions. Each plaintext is equal to the ciphertext of
+ * the previous encryption. The key is made up from the ciphertext
+ * two and three encryptions ago. Both plaintext and key start
+ * at the zero value.
+ * We should have designed a cleaner recurrence relation for
+ * these tests, but it is too late for that now. At least we learned
+ * how to do it better next time.
+ * For details see appendix B of the book.
+ *
+ * Arguments:
+ * key_len Number of bytes of key
+ * final_value Final plaintext value after 49 iterations
+ */
+static int test_sequence( int key_len, Twofish_Byte final_value[] )
+ {
+ Twofish_Byte buf[ (50+3)*16 ]; /* Buffer to hold our computation values. */
+ Twofish_Byte tmp[16]; /* Temp for testing the decryption. */
+ Twofish_key xkey; /* The expanded key */
+ int i, ret;
+ Twofish_Byte * p;
+
+ /* Wipe the buffer */
+ memset( buf, 0, sizeof( buf ) );
+
+ /*
+ * Because the recurrence relation is done in an inconvenient manner
+ * we end up looping backwards over the buffer.
+ */
+
+ /* Pointer in buffer points to current plaintext. */
+ p = &buf[50*16];
+ for( i=1; i<50; i++ )
+ {
+ /*
+ * Prepare a key.
+ * This automatically checks that key_len is valid.
+ */
+ if ((ret = Twofish_prepare_key( p+16, key_len, &xkey)) < 0)
+ return ret;
+
+ /* Compute the next 16 bytes in the buffer */
+ Twofish_encrypt( &xkey, p, p-16 );
+
+ /* Check that the decryption is correct. */
+ Twofish_decrypt( &xkey, p-16, tmp );
+ if( memcmp( tmp, p, 16 ) != 0 )
+ {
+ Twofish_fatal( "Twofish decryption failure in sequence", ERR_SEQ_DEC );
+ }
+ /* Move on to next 16 bytes in the buffer. */
+ p -= 16;
+ }
+
+ /* And check the final value. */
+ if( memcmp( p, final_value, 16 ) != 0 )
+ {
+ Twofish_fatal( "Twofish encryption failure in sequence", ERR_SEQ_ENC );
+ }
+
+ /* None of the data was secret, so there is no need to wipe anything. */
+ return SUCCESS;
+ }
+
+
+/*
+ * Run all three sequence tests from the Twofish test vectors.
+ *
+ * This checks the most extensive test vectors currently available
+ * for Twofish. The data is from the Twofish book, appendix B.2.
+ */
+static int test_sequences()
+ {
+ static Twofish_Byte r128[] = {
+ 0x5D, 0x9D, 0x4E, 0xEF, 0xFA, 0x91, 0x51, 0x57,
+ 0x55, 0x24, 0xF1, 0x15, 0x81, 0x5A, 0x12, 0xE0
+ };
+ static Twofish_Byte r192[] = {
+ 0xE7, 0x54, 0x49, 0x21, 0x2B, 0xEE, 0xF9, 0xF4,
+ 0xA3, 0x90, 0xBD, 0x86, 0x0A, 0x64, 0x09, 0x41
+ };
+ static Twofish_Byte r256[] = {
+ 0x37, 0xFE, 0x26, 0xFF, 0x1C, 0xF6, 0x61, 0x75,
+ 0xF5, 0xDD, 0xF4, 0xC3, 0x3B, 0x97, 0xA2, 0x05
+ };
+
+ /* Run the three sequence test vectors */
+ int ret;
+ if ((ret = test_sequence( 16, r128)) < 0)
+ return ret;
+ if ((ret = test_sequence( 24, r192)) < 0)
+ return ret;
+ if ((ret = test_sequence( 32, r256)) < 0)
+ return ret;
+ return SUCCESS;
+ }
+
+
+/*
+ * Test the odd-sized keys.
+ *
+ * Every odd-sized key is equivalent to a one of 128, 192, or 256 bits.
+ * The equivalent key is found by padding at the end with zero bytes
+ * until a regular key size is reached.
+ *
+ * We just test that the key expansion routine behaves properly.
+ * If the expanded keys are identical, then the encryptions and decryptions
+ * will behave the same.
+ */
+static int test_odd_sized_keys()
+ {
+ Twofish_Byte buf[32];
+ Twofish_key xkey;
+ Twofish_key xkey_two;
+ int i, ret;
+
+ /*
+ * We first create an all-zero key to use as PRNG key.
+ * Normally we would not have to fill the buffer with zeroes, as we could
+ * just pass a zero key length to the Twofish_prepare_key function.
+ * However, this relies on using odd-sized keys, and those are just the
+ * ones we are testing here. We can't use an untested function to test
+ * itself.
+ */
+ memset( buf, 0, sizeof( buf ) );
+ if ((ret = Twofish_prepare_key( buf, 16, &xkey)) < 0)
+ return ret;
+
+ /* Fill buffer with pseudo-random data derived from two encryptions */
+ Twofish_encrypt( &xkey, buf, buf );
+ Twofish_encrypt( &xkey, buf, buf+16 );
+
+ /* Create all possible shorter keys that are prefixes of the buffer. */
+ for( i=31; i>=0; i-- )
+ {
+ /* Set a byte to zero. This is the new padding byte */
+ buf[i] = 0;
+
+ /* Expand the key with only i bytes of length */
+ if ((ret = Twofish_prepare_key( buf, i, &xkey)) < 0)
+ return ret;
+
+ /* Expand the corresponding padded key of regular length */
+ if ((ret = Twofish_prepare_key( buf, i<=16 ? 16 : (i<= 24 ? 24 : 32), &xkey_two )) < 0)
+ return ret;
+
+ /* Compare the two */
+ if( memcmp( &xkey, &xkey_two, sizeof( xkey ) ) != 0 )
+ {
+ Twofish_fatal( "Odd sized keys do not expand properly", ERR_ODD_KEY );
+ }
+ }
+
+ /* None of the key values are secret, so we don't need to wipe them. */
+ return SUCCESS;
+ }
+
+
+/*
+ * Test the Twofish implementation.
+ *
+ * This routine runs all the self tests, in order of importance.
+ * It is called by the Twofish_initialise routine.
+ *
+ * In almost all applications the cost of running the self tests during
+ * initialisation is insignificant, especially
+ * compared to the time it takes to load the application from disk.
+ * If you are very pressed for initialisation performance,
+ * you could remove some of the tests. Make sure you did run them
+ * once in the software and hardware configuration you are using.
+ */
+static int self_test()
+ {
+ int ret;
+ /* The three test vectors form an absolute minimal test set. */
+ if ((ret = test_vectors()) < 0)
+ return ret;
+
+ /*
+ * If at all possible you should run these tests too. They take
+ * more time, but provide a more thorough coverage.
+ */
+ if ((ret = test_sequences()) < 0)
+ return ret;
+
+ /* Test the odd-sized keys. */
+ if ((ret = test_odd_sized_keys()) < 0)
+ return ret;
+ return SUCCESS;
+ }
+
+
+/*
+ * And now, the actual Twofish implementation.
+ *
+ * This implementation generates all the tables during initialisation.
+ * I don't like large tables in the code, especially since they are easily
+ * damaged in the source without anyone noticing it. You need code to
+ * generate them anyway, and this way all the code is close together.
+ * Generating them in the application leads to a smaller executable
+ * (the code is smaller than the tables it generates) and a
+ * larger static memory footprint.
+ *
+ * Twofish can be implemented in many ways. I have chosen to
+ * use large tables with a relatively long key setup time.
+ * If you encrypt more than a few blocks of data it pays to pre-compute
+ * as much as possible. This implementation is relatively inefficient for
+ * applications that need to re-key every block or so.
+ */
+
+/*
+ * We start with the t-tables, directly from the Twofish definition.
+ * These are nibble-tables, but merging them and putting them two nibbles
+ * in one byte is more work than it is worth.
+ */
+static Twofish_Byte t_table[2][4][16] = {
+ {
+ {0x8,0x1,0x7,0xD,0x6,0xF,0x3,0x2,0x0,0xB,0x5,0x9,0xE,0xC,0xA,0x4},
+ {0xE,0xC,0xB,0x8,0x1,0x2,0x3,0x5,0xF,0x4,0xA,0x6,0x7,0x0,0x9,0xD},
+ {0xB,0xA,0x5,0xE,0x6,0xD,0x9,0x0,0xC,0x8,0xF,0x3,0x2,0x4,0x7,0x1},
+ {0xD,0x7,0xF,0x4,0x1,0x2,0x6,0xE,0x9,0xB,0x3,0x0,0x8,0x5,0xC,0xA}
+ },
+ {
+ {0x2,0x8,0xB,0xD,0xF,0x7,0x6,0xE,0x3,0x1,0x9,0x4,0x0,0xA,0xC,0x5},
+ {0x1,0xE,0x2,0xB,0x4,0xC,0x3,0x7,0x6,0xD,0xA,0x5,0xF,0x9,0x0,0x8},
+ {0x4,0xC,0x7,0x5,0x1,0x6,0x9,0xA,0x0,0xE,0xD,0x8,0x2,0xB,0x3,0xF},
+ {0xB,0x9,0x5,0x1,0xC,0x3,0xD,0xE,0x6,0x4,0x7,0xF,0x2,0x0,0x8,0xA}
+ }
+};
+
+
+/* A 1-bit rotation of 4-bit values. Input must be in range 0..15 */
+#define ROR4BY1( x ) (((x)>>1) | (((x)<<3) & 0x8) )
+
+/*
+ * The q-boxes are only used during the key schedule computations.
+ * These are 8->8 bit lookup tables. Some CPUs prefer to have 8->32 bit
+ * lookup tables as it is faster to load a 32-bit value than to load an
+ * 8-bit value and zero the rest of the register.
+ * The LARGE_Q_TABLE switch allows you to choose 32-bit entries in
+ * the q-tables. Here we just define the Qtype which is used to store
+ * the entries of the q-tables.
+ */
+#if LARGE_Q_TABLE
+typedef Twofish_UInt32 Qtype;
+#else
+typedef Twofish_Byte Qtype;
+#endif
+
+/*
+ * The actual q-box tables.
+ * There are two q-boxes, each having 256 entries.
+ */
+static Qtype q_table[2][256];
+
+
+/*
+ * Now the function that converts a single t-table into a q-table.
+ *
+ * Arguments:
+ * t[4][16] : four 4->4bit lookup tables that define the q-box
+ * q[256] : output parameter: the resulting q-box as a lookup table.
+ */
+static void make_q_table( Twofish_Byte t[4][16], Qtype q[256] )
+ {
+ int ae,be,ao,bo; /* Some temporaries. */
+ int i;
+ /* Loop over all input values and compute the q-box result. */
+ for( i=0; i<256; i++ ) {
+ /*
+ * This is straight from the Twofish specifications.
+ *
+ * The ae variable is used for the a_i values from the specs
+ * with even i, and ao for the odd i's. Similarly for the b's.
+ */
+ ae = i>>4; be = i&0xf;
+ ao = ae ^ be; bo = ae ^ ROR4BY1(be) ^ ((ae<<3)&8);
+ ae = t[0][ao]; be = t[1][bo];
+ ao = ae ^ be; bo = ae ^ ROR4BY1(be) ^ ((ae<<3)&8);
+ ae = t[2][ao]; be = t[3][bo];
+
+ /* Store the result in the q-box table, the cast avoids a warning. */
+ q[i] = (Qtype) ((be<<4) | ae);
+ }
+ }
+
+
+/*
+ * Initialise both q-box tables.
+ */
+static void initialise_q_boxes() {
+ /* Initialise each of the q-boxes using the t-tables */
+ make_q_table( t_table[0], q_table[0] );
+ make_q_table( t_table[1], q_table[1] );
+ }
+
+
+/*
+ * Next up is the MDS matrix multiplication.
+ * The MDS matrix multiplication operates in the field
+ * GF(2)[x]/p(x) with p(x)=x^8+x^6+x^5+x^3+1.
+ * If you don't understand this, read a book on finite fields. You cannot
+ * follow the finite-field computations without some background.
+ *
+ * In this field, multiplication by x is easy: shift left one bit
+ * and if bit 8 is set then xor the result with 0x169.
+ *
+ * The MDS coefficients use a multiplication by 1/x,
+ * or rather a division by x. This is easy too: first make the
+ * value 'even' (i.e. bit 0 is zero) by xorring with 0x169 if necessary,
+ * and then shift right one position.
+ * Even easier: shift right and xor with 0xb4 if the lsbit was set.
+ *
+ * The MDS coefficients are 1, EF, and 5B, and we use the fact that
+ * EF = 1 + 1/x + 1/x^2
+ * 5B = 1 + 1/x^2
+ * in this field. This makes multiplication by EF and 5B relatively easy.
+ *
+ * This property is no accident, the MDS matrix was designed to allow
+ * this implementation technique to be used.
+ *
+ * We have four MDS tables, each mapping 8 bits to 32 bits.
+ * Each table performs one column of the matrix multiplication.
+ * As the MDS is always preceded by q-boxes, each of these tables
+ * also implements the q-box just previous to that column.
+ */
+
+/* The actual MDS tables. */
+static Twofish_UInt32 MDS_table[4][256];
+
+/* A small table to get easy conditional access to the 0xb4 constant. */
+static Twofish_UInt32 mds_poly_divx_const[] = {0,0xb4};
+
+/* Function to initialise the MDS tables. */
+static void initialise_mds_tables()
+ {
+ int i;
+ Twofish_UInt32 q,qef,q5b; /* Temporary variables. */
+
+ /* Loop over all 8-bit input values */
+ for( i=0; i<256; i++ )
+ {
+ /*
+ * To save some work during the key expansion we include the last
+ * of the q-box layers from the h() function in these MDS tables.
+ */
+
+ /* We first do the inputs that are mapped through the q0 table. */
+ q = q_table[0][i];
+ /*
+ * Here we divide by x, note the table to get 0xb4 only if the
+ * lsbit is set.
+ * This sets qef = (1/x)*q in the finite field
+ */
+ qef = (q >> 1) ^ mds_poly_divx_const[ q & 1 ];
+ /*
+ * Divide by x again, and add q to get (1+1/x^2)*q.
+ * Note that (1+1/x^2) = 5B in the field, and addition in the field
+ * is exclusive or on the bits.
+ */
+ q5b = (qef >> 1) ^ mds_poly_divx_const[ qef & 1 ] ^ q;
+ /*
+ * Add q5b to qef to set qef = (1+1/x+1/x^2)*q.
+ * Again, (1+1/x+1/x^2) = EF in the field.
+ */
+ qef ^= q5b;
+
+ /*
+ * Now that we have q5b = 5B * q and qef = EF * q
+ * we can fill two of the entries in the MDS matrix table.
+ * See the Twofish specifications for the order of the constants.
+ */
+ MDS_table[1][i] = (q <<24) | (q5b<<16) | (qef<<8) | qef;
+ MDS_table[3][i] = (q5b<<24) | (qef<<16) | (q <<8) | q5b;
+
+ /* Now we do it all again for the two columns that have a q1 box. */
+ q = q_table[1][i];
+ qef = (q >> 1) ^ mds_poly_divx_const[ q & 1 ];
+ q5b = (qef >> 1) ^ mds_poly_divx_const[ qef & 1 ] ^ q;
+ qef ^= q5b;
+
+ /* The other two columns use the coefficient in a different order. */
+ MDS_table[0][i] = (qef<<24) | (qef<<16) | (q5b<<8) | q ;
+ MDS_table[2][i] = (qef<<24) | (q <<16) | (qef<<8) | q5b;
+ }
+ }
+
+
+/*
+ * The h() function is the heart of the Twofish cipher.
+ * It is a complicated sequence of q-box lookups, key material xors,
+ * and finally the MDS matrix.
+ * We use lots of macros to make this reasonably fast.
+ */
+
+/* First a shorthand for the two q-tables */
+#define q0 q_table[0]
+#define q1 q_table[1]
+
+/*
+ * Each macro computes one column of the h for either 2, 3, or 4 stages.
+ * As there are 4 columns, we have 12 macros in all.
+ *
+ * The key bytes are stored in the Byte array L at offset
+ * 0,1,2,3, 8,9,10,11, [16,17,18,19, [24,25,26,27]] as this is the
+ * order we get the bytes from the user. If you look at the Twofish
+ * specs, you'll see that h() is applied to the even key words or the
+ * odd key words. The bytes of the even words appear in this spacing,
+ * and those of the odd key words too.
+ *
+ * These macros are the only place where the q-boxes and the MDS table
+ * are used.
+ */
+#define H02( y, L ) MDS_table[0][q0[q0[y]^L[ 8]]^L[0]]
+#define H12( y, L ) MDS_table[1][q0[q1[y]^L[ 9]]^L[1]]
+#define H22( y, L ) MDS_table[2][q1[q0[y]^L[10]]^L[2]]
+#define H32( y, L ) MDS_table[3][q1[q1[y]^L[11]]^L[3]]
+#define H03( y, L ) H02( q1[y]^L[16], L )
+#define H13( y, L ) H12( q1[y]^L[17], L )
+#define H23( y, L ) H22( q0[y]^L[18], L )
+#define H33( y, L ) H32( q0[y]^L[19], L )
+#define H04( y, L ) H03( q1[y]^L[24], L )
+#define H14( y, L ) H13( q0[y]^L[25], L )
+#define H24( y, L ) H23( q0[y]^L[26], L )
+#define H34( y, L ) H33( q1[y]^L[27], L )
+
+/*
+ * Now we can define the h() function given an array of key bytes.
+ * This function is only used in the key schedule, and not to pre-compute
+ * the keyed S-boxes.
+ *
+ * In the key schedule, the input is always of the form k*(1+2^8+2^16+2^24)
+ * so we only provide k as an argument.
+ *
+ * Arguments:
+ * k input to the h() function.
+ * L pointer to array of key bytes at
+ * offsets 0,1,2,3, ... 8,9,10,11, [16,17,18,19, [24,25,26,27]]
+ * kCycles # key cycles, 2, 3, or 4.
+ */
+static Twofish_UInt32 h( int k, Twofish_Byte L[], int kCycles )
+ {
+ switch( kCycles ) {
+ /* We code all 3 cases separately for speed reasons. */
+ case 2:
+ return H02(k,L) ^ H12(k,L) ^ H22(k,L) ^ H32(k,L);
+ case 3:
+ return H03(k,L) ^ H13(k,L) ^ H23(k,L) ^ H33(k,L);
+ case 4:
+ return H04(k,L) ^ H14(k,L) ^ H24(k,L) ^ H34(k,L);
+ default:
+ /* This is always a coding error, which is fatal. */
+ Twofish_fatal( "Twofish h(): Illegal argument", ERR_ILL_ARG );
+ return ERR_ILL_ARG;
+ }
+ }
+
+
+/*
+ * Pre-compute the keyed S-boxes.
+ * Fill the pre-computed S-box array in the expanded key structure.
+ * Each pre-computed S-box maps 8 bits to 32 bits.
+ *
+ * The S argument contains half the number of bytes of the full key, but is
+ * derived from the full key. (See Twofish specifications for details.)
+ * S has the weird byte input order used by the Hxx macros.
+ *
+ * This function takes most of the time of a key expansion.
+ *
+ * Arguments:
+ * S pointer to array of 8*kCycles Bytes containing the S vector.
+ * kCycles number of key words, must be in the set {2,3,4}
+ * xkey pointer to Twofish_key structure that will contain the S-boxes.
+ */
+static int fill_keyed_sboxes( Twofish_Byte S[], int kCycles, Twofish_key * xkey )
+ {
+ int i;
+ switch( kCycles ) {
+ /* We code all 3 cases separately for speed reasons. */
+ case 2:
+ for( i=0; i<256; i++ )
+ {
+ xkey->s[0][i]= H02( i, S );
+ xkey->s[1][i]= H12( i, S );
+ xkey->s[2][i]= H22( i, S );
+ xkey->s[3][i]= H32( i, S );
+ }
+ break;
+ case 3:
+ for( i=0; i<256; i++ )
+ {
+ xkey->s[0][i]= H03( i, S );
+ xkey->s[1][i]= H13( i, S );
+ xkey->s[2][i]= H23( i, S );
+ xkey->s[3][i]= H33( i, S );
+ }
+ break;
+ case 4:
+ for( i=0; i<256; i++ )
+ {
+ xkey->s[0][i]= H04( i, S );
+ xkey->s[1][i]= H14( i, S );
+ xkey->s[2][i]= H24( i, S );
+ xkey->s[3][i]= H34( i, S );
+ }
+ break;
+ default:
+ /* This is always a coding error, which is fatal. */
+ Twofish_fatal( "Twofish fill_keyed_sboxes(): Illegal argument", ERR_ILL_ARG );
+ }
+ return SUCCESS;
+ }
+
+
+/* A flag to keep track of whether we have been initialised or not. */
+static int Twofish_initialised = 0;
+
+/*
+ * Initialise the Twofish implementation.
+ * This function must be called before any other function in the
+ * Twofish implementation is called.
+ * This routine also does some sanity checks, to make sure that
+ * all the macros behave, and it tests the whole cipher.
+ */
+int Twofish_initialise()
+ {
+ int ret;
+ /* First test the various platform-specific definitions. */
+ if ((ret = test_platform()) < 0)
+ return ret;
+
+ /* We can now generate our tables, in the right order of course. */
+ initialise_q_boxes();
+ initialise_mds_tables();
+
+ /* We're finished with the initialisation itself. */
+ Twofish_initialised = 1;
+
+ /*
+ * And run some tests on the whole cipher.
+ * Yes, you need to do this every time you start your program.
+ * It is called assurance; you have to be certain that your program
+ * still works properly.
+ */
+ return self_test();
+ }
+
+
+/*
+ * The Twofish key schedule uses an Reed-Solomon code matrix multiply.
+ * Just like the MDS matrix, the RS-matrix is designed to be easy
+ * to implement. Details are below in the code.
+ *
+ * These constants make it easy to compute in the finite field used
+ * for the RS code.
+ *
+ * We use Bytes for the RS computation, but these are automatically
+ * widened to unsigned integers in the expressions. Having unsigned
+ * ints in these tables therefore provides the fastest access.
+ */
+static unsigned int rs_poly_const[] = {0, 0x14d};
+static unsigned int rs_poly_div_const[] = {0, 0xa6 };
+
+
+/*
+ * Prepare a key for use in encryption and decryption.
+ * Like most block ciphers, Twofish allows the key schedule
+ * to be pre-computed given only the key.
+ * Twofish has a fairly 'heavy' key schedule that takes a lot of time
+ * to compute. The main work is pre-computing the S-boxes used in the
+ * encryption and decryption. We feel that this makes the cipher much
+ * harder to attack. The attacker doesn't even know what the S-boxes
+ * contain without including the entire key schedule in the analysis.
+ *
+ * Unlike most Twofish implementations, this one allows any key size from
+ * 0 to 32 bytes. Odd key sizes are defined for Twofish (see the
+ * specifications); the key is simply padded with zeroes to the next real
+ * key size of 16, 24, or 32 bytes.
+ * Each odd-sized key is thus equivalent to a single normal-sized key.
+ *
+ * Arguments:
+ * key array of key bytes
+ * key_len number of bytes in the key, must be in the range 0,...,32.
+ * xkey Pointer to an Twofish_key structure that will be filled
+ * with the internal form of the cipher key.
+ */
+int Twofish_prepare_key( Twofish_Byte key[], int key_len, Twofish_key * xkey )
+ {
+ /* We use a single array to store all key material in,
+ * to simplify the wiping of the key material at the end.
+ * The first 32 bytes contain the actual (padded) cipher key.
+ * The next 32 bytes contain the S-vector in its weird format,
+ * and we have 4 bytes of overrun necessary for the RS-reduction.
+ */
+ Twofish_Byte K[32+32+4];
+
+ int kCycles; /* # key cycles, 2,3, or 4. */
+
+ int i;
+ Twofish_UInt32 A, B; /* Used to compute the round keys. */
+
+ Twofish_Byte * kptr; /* Three pointers for the RS computation. */
+ Twofish_Byte * sptr;
+ Twofish_Byte * t;
+
+ Twofish_Byte b,bx,bxx; /* Some more temporaries for the RS computation. */
+
+ /* Check that the Twofish implementation was initialised. */
+ if( Twofish_initialised == 0 )
+ {
+ /*
+ * You didn't call Twofish_initialise before calling this routine.
+ * This is a programming error, and therefore we call the fatal
+ * routine.
+ *
+ * I could of course call the initialisation routine here,
+ * but there are a few reasons why I don't. First of all, the
+ * self-tests have to be done at startup. It is no good to inform
+ * the user that the cipher implementation fails when he wants to
+ * write his data to disk in encrypted form. You have to warn him
+ * before he spends time typing his data. Second, the initialisation
+ * and self test are much slower than a single key expansion.
+ * Calling the initialisation here makes the performance of the
+ * cipher unpredictable. This can lead to really weird problems
+ * if you use the cipher for a real-time task. Suddenly it fails
+ * once in a while the first time you try to use it. Things like
+ * that are almost impossible to debug.
+ */
+ /* Twofish_fatal( "Twofish implementation was not initialised.", ERR_INIT ); */
+
+ /*
+ * There is always a danger that the Twofish_fatal routine returns,
+ * in spite of the specifications that it should not.
+ * (A good programming rule: don't trust the rest of the code.)
+ * This would be disasterous. If the q-tables and MDS-tables have
+ * not been initialised, they are probably still filled with zeroes.
+ * Suppose the MDS-tables are all zero. The key expansion would then
+ * generate all-zero round keys, and all-zero s-boxes. The danger
+ * is that nobody would notice as the encry
+ * mangles the input, and the decryption still 'decrypts' it,
+ * but now in a completely key-independent manner.
+ * To stop such security disasters, we use blunt force.
+ * If your program hangs here: fix the fatal routine!
+ */
+ for(;;); /* Infinite loop, which beats being insecure. */
+ }
+
+ /* Check for valid key length. */
+ if( key_len < 0 || key_len > 32 )
+ {
+ /*
+ * This can only happen if a programmer didn't read the limitations
+ * on the key size.
+ */
+ Twofish_fatal( "Twofish_prepare_key: illegal key length", ERR_KEY_LEN );
+ /*
+ * A return statement just in case the fatal macro returns.
+ * The rest of the code assumes that key_len is in range, and would
+ * buffer-overflow if it wasn't.
+ *
+ * Why do we still use a programming language that has problems like
+ * buffer overflows, when these problems were solved in 1960 with
+ * the development of Algol? Have we not leared anything?
+ */
+ return ERR_KEY_LEN;
+ }
+
+ /* Pad the key with zeroes to the next suitable key length. */
+ memcpy( K, key, key_len );
+ memset( K+key_len, 0, sizeof(K)-key_len );
+
+ /*
+ * Compute kCycles: the number of key cycles used in the cipher.
+ * 2 for 128-bit keys, 3 for 192-bit keys, and 4 for 256-bit keys.
+ */
+ kCycles = (key_len + 7) >> 3;
+ /* Handle the special case of very short keys: minimum 2 cycles. */
+ if( kCycles < 2 )
+ {
+ kCycles = 2;
+ }
+
+ /*
+ * From now on we just pretend to have 8*kCycles bytes of
+ * key material in K. This handles all the key size cases.
+ */
+
+ /*
+ * We first compute the 40 expanded key words,
+ * formulas straight from the Twofish specifications.
+ */
+ for( i=0; i<40; i+=2 )
+ {
+ /*
+ * Due to the byte spacing expected by the h() function
+ * we can pick the bytes directly from the key K.
+ * As we use bytes, we never have the little/big endian
+ * problem.
+ *
+ * Note that we apply the rotation function only to simple
+ * variables, as the rotation macro might evaluate its argument
+ * more than once.
+ */
+ A = h( i , K , kCycles );
+ B = h( i+1, K+4, kCycles );
+ B = ROL32( B, 8 );
+
+ /* Compute and store the round keys. */
+ A += B;
+ B += A;
+ xkey->K[i] = A;
+ xkey->K[i+1] = ROL32( B, 9 );
+ }
+
+ /* Wipe variables that contained key material. */
+ A=B=0;
+
+ /*
+ * And now the dreaded RS multiplication that few seem to understand.
+ * The RS matrix is not random, and is specially designed to compute the
+ * RS matrix multiplication in a simple way.
+ *
+ * We work in the field GF(2)[x]/x^8+x^6+x^3+x^2+1. Note that this is a
+ * different field than used for the MDS matrix.
+ * (At least, it is a different representation because all GF(2^8)
+ * representations are equivalent in some form.)
+ *
+ * We take 8 consecutive bytes of the key and interpret them as
+ * a polynomial k_0 + k_1 y + k_2 y^2 + ... + k_7 y^7 where
+ * the k_i bytes are the key bytes and are elements of the finite field.
+ * We multiply this polynomial by y^4 and reduce it modulo
+ * y^4 + (x + 1/x)y^3 + (x)y^2 + (x + 1/x)y + 1.
+ * using straightforward polynomial modulo reduction.
+ * The coefficients of the result are the result of the RS
+ * matrix multiplication. When we wrote the Twofish specification,
+ * the original RS definition used the polynomials,
+ * but that requires much more mathematical knowledge.
+ * We were already using matrix multiplication in a finite field for
+ * the MDS matrix, so I re-wrote the RS operation as a matrix
+ * multiplication to reduce the difficulty of understanding it.
+ * Some implementors have not picked up on this simpler method of
+ * computing the RS operation, even though it is mentioned in the
+ * specifications.
+ *
+ * It is possible to perform these computations faster by using 32-bit
+ * word operations, but that is not portable and this is not a speed-
+ * critical area.
+ *
+ * We explained the 1/x computation when we did the MDS matrix.
+ *
+ * The S vector is stored in K[32..64].
+ * The S vector has to be reversed, so we loop cross-wise.
+ *
+ * Note the weird byte spacing of the S-vector, to match the even
+ * or odd key words arrays. See the discussion at the Hxx macros for
+ * details.
+ */
+ kptr = K + 8*kCycles; /* Start at end of key */
+ sptr = K + 32; /* Start at start of S */
+
+ /* Loop over all key material */
+ while( kptr > K )
+ {
+ kptr -= 8;
+ /*
+ * Initialise the polynimial in sptr[0..12]
+ * The first four coefficients are 0 as we have to multiply by y^4.
+ * The next 8 coefficients are from the key material.
+ */
+ memset( sptr, 0, 4 );
+ memcpy( sptr+4, kptr, 8 );
+
+ /*
+ * The 12 bytes starting at sptr are now the coefficients of
+ * the polynomial we need to reduce.
+ */
+
+ /* Loop over the polynomial coefficients from high to low */
+ t = sptr+11;
+ /* Keep looping until polynomial is degree 3; */
+ while( t > sptr+3 )
+ {
+ /* Pick up the highest coefficient of the poly. */
+ b = *t;
+
+ /*
+ * Compute x and (x+1/x) times this coefficient.
+ * See the MDS matrix implementation for a discussion of
+ * multiplication by x and 1/x. We just use different
+ * constants here as we are in a
+ * different finite field representation.
+ *
+ * These two statements set
+ * bx = (x) * b
+ * bxx= (x + 1/x) * b
+ */
+ bx = (Twofish_Byte)((b<<1) ^ rs_poly_const[ b>>7 ]);
+ bxx= (Twofish_Byte)((b>>1) ^ rs_poly_div_const[ b&1 ] ^ bx);
+
+ /*
+ * Subtract suitable multiple of
+ * y^4 + (x + 1/x)y^3 + (x)y^2 + (x + 1/x)y + 1
+ * from the polynomial, except that we don't bother
+ * updating t[0] as it will become zero anyway.
+ */
+ t[-1] ^= bxx;
+ t[-2] ^= bx;
+ t[-3] ^= bxx;
+ t[-4] ^= b;
+
+ /* Go to the next coefficient. */
+ t--;
+ }
+
+ /* Go to next S-vector word, obeying the weird spacing rules. */
+ sptr += 8;
+ }
+
+ /* Wipe variables that contained key material. */
+ b = bx = bxx = 0;
+
+ /* And finally, we can compute the key-dependent S-boxes. */
+ fill_keyed_sboxes( &K[32], kCycles, xkey );
+
+ /* Wipe array that contained key material. */
+ memset( K, 0, sizeof( K ) );
+ return SUCCESS;
+ }
+
+
+/*
+ * We can now start on the actual encryption and decryption code.
+ * As these are often speed-critical we will use a lot of macros.
+ */
+
+/*
+ * The g() function is the heart of the round function.
+ * We have two versions of the g() function, one without an input
+ * rotation and one with.
+ * The pre-computed S-boxes make this pretty simple.
+ */
+#define g0(X,xkey) \
+ (xkey->s[0][b0(X)]^xkey->s[1][b1(X)]^xkey->s[2][b2(X)]^xkey->s[3][b3(X)])
+
+#define g1(X,xkey) \
+ (xkey->s[0][b3(X)]^xkey->s[1][b0(X)]^xkey->s[2][b1(X)]^xkey->s[3][b2(X)])
+
+/*
+ * A single round of Twofish. The A,B,C,D are the four state variables,
+ * T0 and T1 are temporaries, xkey is the expanded key, and r the
+ * round number.
+ *
+ * Note that this macro does not implement the swap at the end of the round.
+ */
+#define ENCRYPT_RND( A,B,C,D, T0, T1, xkey, r ) \
+ T0 = g0(A,xkey); T1 = g1(B,xkey);\
+ C ^= T0+T1+xkey->K[8+2*(r)]; C = ROR32(C,1);\
+ D = ROL32(D,1); D ^= T0+2*T1+xkey->K[8+2*(r)+1]
+
+/*
+ * Encrypt a single cycle, consisting of two rounds.
+ * This avoids the swapping of the two halves.
+ * Parameter r is now the cycle number.
+ */
+#define ENCRYPT_CYCLE( A, B, C, D, T0, T1, xkey, r ) \
+ ENCRYPT_RND( A,B,C,D,T0,T1,xkey,2*(r) );\
+ ENCRYPT_RND( C,D,A,B,T0,T1,xkey,2*(r)+1 )
+
+/* Full 16-round encryption */
+#define ENCRYPT( A,B,C,D,T0,T1,xkey ) \
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 0 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 1 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 2 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 3 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 4 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 5 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 6 );\
+ ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 7 )
+
+/*
+ * A single round of Twofish for decryption. It differs from
+ * ENCRYTP_RND only because of the 1-bit rotations.
+ */
+#define DECRYPT_RND( A,B,C,D, T0, T1, xkey, r ) \
+ T0 = g0(A,xkey); T1 = g1(B,xkey);\
+ C = ROL32(C,1); C ^= T0+T1+xkey->K[8+2*(r)];\
+ D ^= T0+2*T1+xkey->K[8+2*(r)+1]; D = ROR32(D,1)
+
+/*
+ * Decrypt a single cycle, consisting of two rounds.
+ * This avoids the swapping of the two halves.
+ * Parameter r is now the cycle number.
+ */
+#define DECRYPT_CYCLE( A, B, C, D, T0, T1, xkey, r ) \
+ DECRYPT_RND( A,B,C,D,T0,T1,xkey,2*(r)+1 );\
+ DECRYPT_RND( C,D,A,B,T0,T1,xkey,2*(r) )
+
+/* Full 16-round decryption. */
+#define DECRYPT( A,B,C,D,T0,T1, xkey ) \
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 7 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 6 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 5 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 4 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 3 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 2 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 1 );\
+ DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 0 )
+
+/*
+ * A macro to read the state from the plaintext and do the initial key xors.
+ * The koff argument allows us to use the same macro
+ * for the decryption which uses different key words at the start.
+ */
+#define GET_INPUT( src, A,B,C,D, xkey, koff ) \
+ A = GET32(src )^xkey->K[ koff]; B = GET32(src+ 4)^xkey->K[1+koff]; \
+ C = GET32(src+ 8)^xkey->K[2+koff]; D = GET32(src+12)^xkey->K[3+koff]
+
+/*
+ * Similar macro to put the ciphertext in the output buffer.
+ * We xor the keys into the state variables before we use the PUT32
+ * macro as the macro might use its argument multiple times.
+ */
+#define PUT_OUTPUT( A,B,C,D, dst, xkey, koff ) \
+ A ^= xkey->K[ koff]; B ^= xkey->K[1+koff]; \
+ C ^= xkey->K[2+koff]; D ^= xkey->K[3+koff]; \
+ PUT32( A, dst ); PUT32( B, dst+ 4 ); \
+ PUT32( C, dst+8 ); PUT32( D, dst+12 )
+
+
+/*
+ * Twofish block encryption
+ *
+ * Arguments:
+ * xkey expanded key array
+ * p 16 bytes of plaintext
+ * c 16 bytes in which to store the ciphertext
+ */
+void Twofish_encrypt( Twofish_key * xkey, Twofish_Byte p[16], Twofish_Byte c[16])
+ {
+ Twofish_UInt32 A,B,C,D,T0,T1; /* Working variables */
+
+ /* Get the four plaintext words xorred with the key */
+ GET_INPUT( p, A,B,C,D, xkey, 0 );
+
+ /* Do 8 cycles (= 16 rounds) */
+ ENCRYPT( A,B,C,D,T0,T1,xkey );
+
+ /* Store them with the final swap and the output whitening. */
+ PUT_OUTPUT( C,D,A,B, c, xkey, 4 );
+ }
+
+
+/*
+ * Twofish block decryption.
+ *
+ * Arguments:
+ * xkey expanded key array
+ * p 16 bytes of plaintext
+ * c 16 bytes in which to store the ciphertext
+ */
+void Twofish_decrypt( Twofish_key * xkey, Twofish_Byte c[16], Twofish_Byte p[16])
+ {
+ Twofish_UInt32 A,B,C,D,T0,T1; /* Working variables */
+
+ /* Get the four plaintext words xorred with the key */
+ GET_INPUT( c, A,B,C,D, xkey, 4 );
+
+ /* Do 8 cycles (= 16 rounds) */
+ DECRYPT( A,B,C,D,T0,T1,xkey );
+
+ /* Store them with the final swap and the output whitening. */
+ PUT_OUTPUT( C,D,A,B, p, xkey, 0 );
+ }
+
+/*
+ * Using the macros it is easy to make special routines for
+ * CBC mode, CTR mode etc. The only thing you might want to
+ * add is a XOR_PUT_OUTPUT which xors the outputs into the
+ * destinationa instead of overwriting the data. This requires
+ * a XOR_PUT32 macro as well, but that should all be trivial.
+ *
+ * I thought about including routines for the separate cipher
+ * modes here, but it is unclear which modes should be included,
+ * and each encryption or decryption routine takes up a lot of code space.
+ * Also, I don't have any test vectors for any cipher modes
+ * with Twofish.
+ */
+
+
diff --git a/src/libzrtpcpp/crypto/twofish.h b/src/libzrtpcpp/crypto/twofish.h new file mode 100755 index 0000000..0c8b0d7 --- /dev/null +++ b/src/libzrtpcpp/crypto/twofish.h @@ -0,0 +1,265 @@ +/*
+ * Fast, portable, and easy-to-use Twofish implementation,
+ * Version 0.3.
+ * Copyright (c) 2002 by Niels Ferguson.
+ *
+ * See the twofish.c file for the details of the how and why of this code.
+ *
+ * The author hereby grants a perpetual license to everybody to
+ * use this code for any purpose as long as the copyright message is included
+ * in the source code of this or any derived work.
+ */
+
+
+/*
+ * PLATFORM FIXES
+ * ==============
+ *
+ * The following definitions have to be fixed for each particular platform
+ * you work on. If you have a multi-platform program, you no doubt have
+ * portable definitions that you can substitute here without changing
+ * the rest of the code.
+ *
+ * The defaults provided here should work on most PC compilers.
+ */
+
+#ifndef TWOFISH_H
+#define TWOFISH_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @file twofish.h
+ * @brief Function that provide basic Twofish crypto support
+ *
+ * @ingroup GNU_ZRTP
+ * @{
+ */
+
+/**
+ * A Twofish_Byte must be an unsigned 8-bit integer.
+ *
+ * It must also be the elementary data size of your C platform,
+ * i.e. sizeof( Twofish_Byte ) == 1.
+ */
+typedef unsigned char Twofish_Byte;
+
+/**
+ * A Twofish_UInt32 must be an unsigned integer of at least 32 bits.
+ *
+ * This type is used only internally in the implementation, so ideally it
+ * would not appear in the header file, but it is used inside the
+ * Twofish_key structure which means it has to be included here.
+ */
+typedef unsigned int Twofish_UInt32;
+
+
+/*
+ * END OF PLATFORM FIXES
+ * =====================
+ *
+ * You should not have to touch the rest of this file, but the code
+ * in twofish.c has a few things you need to fix too.
+ */
+
+/**
+ * Return codes
+ */
+#define SUCCESS 1
+#define ERR_UINT32 -2
+#define ERR_BYTE -3
+#define ERR_GET32 -4
+#define ERR_PUT32 -5
+#define ERR_ROLR -6
+#define ERR_BSWAP -7
+#define ERR_SELECTB -8
+#define ERR_TEST_ENC -9
+#define ERR_TEST_DEC -10
+#define ERR_SEQ_ENC -11
+#define ERR_SEQ_DEC -12
+#define ERR_ODD_KEY -13
+#define ERR_INIT -14
+#define ERR_KEY_LEN -15
+#define ERR_ILL_ARG -16
+
+
+/**
+ * Structure that contains a prepared Twofish key.
+ *
+ * A cipher key is used in two stages. In the first stage it is converted
+ * form the original form to an internal representation.
+ * This internal form is then used to encrypt and decrypt data.
+ * This structure contains the internal form. It is rather large: 4256 bytes
+ * on a platform with 32-bit unsigned values.
+ *
+ * Treat this as an opague structure, and don't try to manipulate the
+ * elements in it. I wish I could hide the inside of the structure,
+ * but C doesn't allow that.
+ */
+typedef
+ struct
+ {
+ Twofish_UInt32 s[4][256]; /* pre-computed S-boxes */
+ Twofish_UInt32 K[40]; /* Round key words */
+ }
+ Twofish_key;
+
+
+/**
+ * Initialise and test the Twofish implementation.
+ *
+ * This function MUST be called before any other function in the
+ * Twofish implementation is called.
+ * It only needs to be called once.
+ *
+ * Apart from initialising the implementation it performs a self test.
+ * If the Twofish_fatal function is not called, the code passed the test.
+ * (See the twofish.c file for details on the Twofish_fatal function.)
+ *
+ * @returns a negative number if an error happend, +1 otherwise
+ */
+extern int Twofish_initialise();
+
+
+/**
+ * Convert a cipher key to the internal form used for
+ * encryption and decryption.
+ *
+ * The cipher key is an array of bytes; the Twofish_Byte type is
+ * defined above to a type suitable on your platform.
+ *
+ * Any key must be converted to an internal form in the Twofisk_key structure
+ * before it can be used.
+ * The encryption and decryption functions only work with the internal form.
+ * The conversion to internal form need only be done once for each key value.
+ *
+ * Be sure to wipe all key storage, including the Twofish_key structure,
+ * once you are done with the key data.
+ * A simple memset( TwofishKey, 0, sizeof( TwofishKey ) ) will do just fine.
+ *
+ * Unlike most implementations, this one allows any key size from 0 bytes
+ * to 32 bytes. According to the Twofish specifications,
+ * irregular key sizes are handled by padding the key with zeroes at the end
+ * until the key size is 16, 24, or 32 bytes, whichever
+ * comes first. Note that each key of irregular size is equivalent to exactly
+ * one key of 16, 24, or 32 bytes.
+ *
+ * WARNING: Short keys have low entropy, and result in low security.
+ * Anything less than 8 bytes is utterly insecure. For good security
+ * use at least 16 bytes. I prefer to use 32-byte keys to prevent
+ * any collision attacks on the key.
+ *
+ * The key length argument key_len must be in the proper range.
+ * If key_len is not in the range 0,...,32 this routine attempts to generate
+ * a fatal error (depending on the code environment),
+ * and at best (or worst) returns without having done anything.
+ *
+ * @param key Array of key bytes
+ * @param key_len Number of key bytes, must be in the range 0,1,...,32.
+ * @param xkey Pointer to an Twofish_key structure that will be filled
+ * with the internal form of the cipher key.
+ * @returns a negative number if an error happend, +1 otherwise
+ */
+extern int Twofish_prepare_key(
+ Twofish_Byte key[],
+ int key_len,
+ Twofish_key * xkey
+ );
+
+
+/**
+ * Encrypt a single block of data.
+ *
+ * This function encrypts a single block of 16 bytes of data.
+ * If you want to encrypt a larger or variable-length message,
+ * you will have to use a cipher mode, such as CBC or CTR.
+ * These are outside the scope of this implementation.
+ *
+ * The xkey structure is not modified by this routine, and can be
+ * used for further encryption and decryption operations.
+ *
+ * @param xkey pointer to Twofish_key, internal form of the key
+ * produces by Twofish_prepare_key()
+ * @param p Plaintext to be encrypted
+ * @param c Place to store the ciphertext
+ */
+extern void Twofish_encrypt(
+ Twofish_key * xkey,
+ Twofish_Byte p[16],
+ Twofish_Byte c[16]
+ );
+
+
+/**
+ * Decrypt a single block of data.
+ *
+ * This function decrypts a single block of 16 bytes of data.
+ * If you want to decrypt a larger or variable-length message,
+ * you will have to use a cipher mode, such as CBC or CTR.
+ * These are outside the scope of this implementation.
+ *
+ * The xkey structure is not modified by this routine, and can be
+ * used for further encryption and decryption operations.
+ *
+ * @param xkey pointer to Twofish_key, internal form of the key
+ * produces by Twofish_prepare_key()
+ * @param c Ciphertext to be decrypted
+ * @param p Place to store the plaintext
+ */
+extern void Twofish_decrypt(
+ Twofish_key * xkey,
+ Twofish_Byte c[16],
+ Twofish_Byte p[16]
+ );
+
+
+/**
+ * Encrypt data in CFB mode.
+ *
+ * This function encrypts data in CFB mode.
+ *
+ * The key structure is not modified by this routine, and can be
+ * used for further encryption and decryption operations.
+ *
+ * @param keyCtx pointer to Twofish_key, internal form of the key
+ * produced by Twofish_prepare_key()
+ * @param in Plaintext to be encrypted
+ * @param out Place to store the ciphertext
+ * @param len number of bytes to encrypt.
+ * @param ivec initialization vector for this CFB mode encryption.
+ * @param num pointer to integer that holds number of available crypto bytes.
+ */
+void Twofish_cfb128_encrypt(Twofish_key* keyCtx, Twofish_Byte* in,
+ Twofish_Byte* out, size_t len,
+ Twofish_Byte* ivec, int *num);
+
+/**
+ * Decrypt data in CFB mode.
+ *
+ * This function decrypts data in CFB.
+ *
+ * The key structure is not modified by this routine, and can be
+ * used for further encryption and decryption operations.
+ *
+ * @param keyCtx pointer to Twofish_key, internal form of the key
+ * produced by Twofish_prepare_key()
+ * @param in Ciphertext to be decrypted
+ * @param out Place to store the plaintext
+ * @param len number of bytes to decrypt.
+ * @param ivec initialization vector for this CFB mode encryption.
+ * @param num pointer to integer that holds number of available crypto bytes.
+ */
+void Twofish_cfb128_decrypt(Twofish_key* keyCtx, Twofish_Byte* in,
+ Twofish_Byte* out, size_t len,
+ Twofish_Byte* ivec, int *num);
+/**
+ * @}
+ */
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libzrtpcpp/crypto/twofish_cfb.c b/src/libzrtpcpp/crypto/twofish_cfb.c new file mode 100755 index 0000000..7540738 --- /dev/null +++ b/src/libzrtpcpp/crypto/twofish_cfb.c @@ -0,0 +1,82 @@ +#include <stdint.h>
+#include <stdlib.h>
+
+#include "twofish.h"
+
+void Twofish_cfb128_encrypt(Twofish_key* keyCtx, Twofish_Byte* in,
+ Twofish_Byte* out, size_t len,
+ Twofish_Byte* ivec, int32_t *num)
+{
+ uint32_t n;
+
+ n = *num;
+
+ do {
+ while (n && len) {
+ *(out++) = ivec[n] ^= *(in++);
+ --len;
+ n = (n+1) % 16;
+ }
+ while (len>=16) {
+ Twofish_encrypt(keyCtx, ivec, ivec);
+ for (n=0; n<16; n+=sizeof(size_t)) {
+ *(size_t*)(out+n) =
+ *(size_t*)(ivec+n) ^= *(size_t*)(in+n);
+ }
+ len -= 16;
+ out += 16;
+ in += 16;
+ }
+ n = 0;
+ if (len) {
+ Twofish_encrypt(keyCtx, ivec, ivec);
+ while (len--) {
+ out[n] = ivec[n] ^= in[n];
+ ++n;
+ }
+ }
+ *num = n;
+ return;
+ } while (0);
+}
+
+
+void Twofish_cfb128_decrypt(Twofish_key* keyCtx, Twofish_Byte* in,
+ Twofish_Byte* out, size_t len,
+ Twofish_Byte* ivec, int32_t *num)
+{
+ uint32_t n;
+
+ n = *num;
+
+ do {
+ while (n && len) {
+ unsigned char c;
+ *(out++) = ivec[n] ^ (c = *(in++)); ivec[n] = c;
+ --len;
+ n = (n+1) % 16;
+ }
+ while (len>=16) {
+ Twofish_encrypt(keyCtx, ivec, ivec);
+ for (n=0; n<16; n+=sizeof(size_t)) {
+ size_t t = *(size_t*)(in+n);
+ *(size_t*)(out+n) = *(size_t*)(ivec+n) ^ t;
+ *(size_t*)(ivec+n) = t;
+ }
+ len -= 16;
+ out += 16;
+ in += 16;
+ }
+ n = 0;
+ if (len) {
+ Twofish_encrypt(keyCtx, ivec, ivec);
+ while (len--) {
+ unsigned char c;
+ out[n] = ivec[n] ^ (c = in[n]); ivec[n] = c;
+ ++n;
+ }
+ }
+ *num = n;
+ return;
+ } while (0);
+}
diff --git a/src/libzrtpcpp/zrtpPacket.h b/src/libzrtpcpp/zrtpPacket.h new file mode 100644 index 0000000..18ba7a1 --- /dev/null +++ b/src/libzrtpcpp/zrtpPacket.h @@ -0,0 +1,342 @@ +/* + Copyright (C) 2006-2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Authors: Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#ifndef ZRTPPACKET_H +#define ZRTPPACKET_H + +/** + * + * @file zrtpPacket.h + * @brief The data structures and definitions for ZRTP messages + * + * This include file defines the ZRTP message structures. Refer to + * chapter 5 of the ZRTP specification which defines the ZRTP messages and + * the transport format. + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdio.h> + +/** + * The following defines match the ZRTP specification, chapter 5 + */ +#define ZRTP_MAGIC 0x5a525450 + +#define ZRTP_WORD_SIZE 4 +#define CRC_SIZE 4 + +#define TYPE_SIZE (2*ZRTP_WORD_SIZE) +#define CLIENT_ID_SIZE (4*ZRTP_WORD_SIZE) +#define HASH_IMAGE_SIZE (8*ZRTP_WORD_SIZE) +#define ZID_SIZE (3*ZRTP_WORD_SIZE) +#define HVI_SIZE (8*ZRTP_WORD_SIZE) +#define HMAC_SIZE (2*ZRTP_WORD_SIZE) +#define ID_SIZE (2*ZRTP_WORD_SIZE) +#define IV_SIZE (4*ZRTP_WORD_SIZE) +#define PING_HASH_SIZE (2*ZRTP_WORD_SIZE) + + +/** + * The ZRTP message header + * + * A complete ZRTP message always consists of the ZRTP header + * and a message specific part. This specific part may have a variable + * length. The length field includes the header. + */ +typedef struct zrtpPacketHeader { + uint16_t zrtpId; ///< Id to identify the message, always 0x505a + uint16_t length; ///< Length of the ZRTP message in words + uint8_t messageType[TYPE_SIZE]; ///< 2 word (8 octest) message type in ASCII +} zrtpPacketHeader_t; + +/** + * Hello message, fixed part. + * + * The complete Hello message consists of ZRTP message header, Hello fixed + * part and a variable part. The Hello class initializes the variable part. + */ +typedef struct Hello { + uint8_t version[ZRTP_WORD_SIZE]; ///< Announces the ZRTP protocol version + uint8_t clientId[CLIENT_ID_SIZE]; ///< A 4 word ASCII identifier of the ZRTP client + uint8_t hashH3[HASH_IMAGE_SIZE]; ///< The last hash of the hash chain (chap. 9) + uint8_t zid[ZID_SIZE]; ///< ZID - 3 word identifier for the ZRTP endpoint + uint8_t flags; ///< flag bits (chap 7.2) + uint8_t lengths[3]; ///< number of algorithms present +} Hello_t; + +/** + * The complete ZRTP Hello message. + */ +typedef struct HelloPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + Hello_t hello; ///< Fixed part of Hello message +} HelloPacket_t; + +/** + * HelloAck message. + * + * The complete HelloAck message consists of ZRTP message header and + * the CRC which is the only HelloAck specific data. + */ +typedef struct HelloAckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} HelloAckPacket_t; + +/** + * Commit message + * + * There are three subtypes of Commit messages, each of which + * has a fixed size. The data structure defines the maximum + * Commit message. During the ZRTP protocol the implementation + * uses fileds according to the use case (DH handshake, + * Multi-stream handshake) and adjusts the length. + */ +typedef struct Commit { + uint8_t hashH2[HASH_IMAGE_SIZE]; ///< The second hash of the hash chain (chap. 9) + uint8_t zid[ZID_SIZE]; ///< ZID - 3 word identifier for the ZRTP endpoint + uint8_t hash[ZRTP_WORD_SIZE]; ///< Commited hash algorithm + uint8_t cipher[ZRTP_WORD_SIZE]; ///< Commited symmetrical cipher algorithm + uint8_t authlengths[ZRTP_WORD_SIZE]; ///< Commited SRTP authentication algorithm + uint8_t pubkey[ZRTP_WORD_SIZE]; ///< Commited key agreement algorithm + uint8_t sas[ZRTP_WORD_SIZE]; ///< Commited SAS algorithm + uint8_t hvi[HVI_SIZE]; ///< Hash value Initiator - chap 4.4.1.1 + uint8_t hmac[HMAC_SIZE]; ///< MAC of the Commit message +} Commit_t; + +/** + * The complete ZRTP Commit message. + */ +typedef struct CommitPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + Commit_t commit; ///< Commit message + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} CommitPacket_t; + +/** + * DHPart1 and DHPart2 messages + * + * The DHPart messages have a variable length. The following struct + * defines the fixed part only. The DHPart class initializes the + * variable part. + */ +typedef struct DHPart { + uint8_t hashH1[HASH_IMAGE_SIZE]; ///< The first hash of the hash chain (chap. 9) + uint8_t rs1Id[ID_SIZE]; ///< Id of first retained secret + uint8_t rs2Id[ID_SIZE]; ///< Id of second retained secret + uint8_t auxSecretId[ID_SIZE]; ///< Id of additional (auxilliary) secret + uint8_t pbxSecretId[ID_SIZE]; ///< Id of PBX secret (chap 7.3.1) +} DHPart_t; + +/** + * The complete ZRTP DHPart message. + */ +typedef struct DHPartPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + DHPart_t dhPart; ///< DHPart message fixed part +} DHPartPacket_t; + +/** + * Confirm1 and Confirm2 messages + * + * The Confirm message have a variable length. The following struct + * defines the fixed part only. The Confirm class initializes the + * variable part. + * + * ZRTP encrypts a part of the Confirm messages, starting at @c hashH0 + * and includes the variable part. + */ +typedef struct Confirm { + uint8_t hmac[HMAC_SIZE]; ///< MAC over the encrypted part of Commit message + uint8_t iv[IV_SIZE]; ///< IV for CFB mode to encrypt part of Commit + uint8_t hashH0[HASH_IMAGE_SIZE]; ///< starting hash of hash chain (chap. 9) + uint8_t filler[2]; ///< Filler bytes + uint8_t sigLength; ///< Length of an optional signature length (chap 7.2) + uint8_t flags; ///< various flags to control behaviour + uint32_t expTime; ///< Expiration time of retained secrets (chap 4.9) +} Confirm_t; + +/** + * The complete ZRTP Confirm message. + */ +typedef struct ConfirmPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + Confirm_t confirm; ///< Confirm message fixed part +} ConfirmPacket_t; + +/** + * Conf2Ack message. + * + * The complete Conf2Ack message consists of ZRTP message header and + * the CRC which is the only Conf2Ack specific data. + */ +typedef struct Conf2AckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} Conf2AckPacket_t; + +/** + * The GoClear message is currently not used in + * GNU ZRTP C++ - not support for GoClear. + */ +typedef struct GoClear { + uint8_t clearHmac[HMAC_SIZE]; ///< no used +} GoClear_t; + +/** + * The complete ZRTP GoClear message - no used. + */ +typedef struct GoClearPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + GoClear_t goClear; ///< not used + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} GoClearPacket_t; + +/** + * The ClearAck message is currently not used in + * GNU ZRTP C++ - not support for GoClear. + */ +typedef struct ClearAckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} ClearAckPacket_t; + +/** + * The Error message + */ +typedef struct Error { + uint32_t errorCode; ///< Error code, see chap 5.9 +} Error_t; + +/** + * The complete ZRTP Error message. + */ +typedef struct ErrorPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + Error_t error; ///< Error message part + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} ErrorPacket_t; + +/** + * ErrorAck message. + * + * The complete ErrorAck message consists of ZRTP message header and + * the CRC which is the only ErrorAck specific data. + */ +typedef struct ErrorAckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} ErrorAckPacket_t; + +/** + * Ping message. + * + * The Ping message has a fixed size. + */ +typedef struct Ping { + uint8_t version[ZRTP_WORD_SIZE]; ///< The ZRTP protocol version + uint8_t epHash[PING_HASH_SIZE]; ///< End point hash, see chap 5.16 +} Ping_t; + +/** + * The complete ZRTP Ping message. + */ +typedef struct PingPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + Ping_t ping; ///< Ping message part + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} PingPacket_t; + +/** + * PingAck message. + * + * The PingAck message has a fixed size. + */ +typedef struct PingAck { + uint8_t version[ZRTP_WORD_SIZE]; ///< The ZRTP protocol version + uint8_t localEpHash[PING_HASH_SIZE]; ///< Local end point hash, see chap 5.16 + uint8_t remoteEpHash[PING_HASH_SIZE]; ///< Remote end point hash, see chap 5.16 + uint32_t ssrc; ///< SSRC copied from the Ping message (RTP packet part) +} PingAck_t; + +/** + * The complete ZRTP PingAck message. + */ +typedef struct PingAckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + PingAck_t pingAck; ///< PingAck message part + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} PingAckPacket_t; + +/** + * SASrelay message + * + * The SASrelay message has a variable length. The following struct + * defines the fixed part only. The SASrelay class initializes the + * variable part. + * + * ZRTP encrypts a part of the SASrelay message, starting at @c hashH0 + * and includes the variable part. + */ +typedef struct SASrelay { + uint8_t hmac[HMAC_SIZE]; ///< MAC over the encrypted part of Commit message + uint8_t iv[IV_SIZE]; ///< IV for CFB mode to encrypt part of Commit + uint8_t filler[2]; ///< Filler bytes + uint8_t sigLength; ///< Length of an optional signature length (chap 7.2) + uint8_t flags; ///< various flags to control behaviour + uint8_t sas[ZRTP_WORD_SIZE]; ///< SAS algorithm to use + uint8_t trustedSasHash[HASH_IMAGE_SIZE]; ///< New trusted SAS hash for enrolled client +} SASrelay_t; + +/** + * The complete ZRTP SASrelay message. + */ +typedef struct SASrelayPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + SASrelay_t sasrelay; ///< SASrelay message fixed part +} SASrelayPacket_t; + +/** + * RelayAck message. + * + * The complete RelayAck message consists of ZRTP message header and + * the CRC which is the only RelayAck specific data. + */ +typedef struct RelayAckPacket { + zrtpPacketHeader_t hdr; ///< ZRTP Header + uint8_t crc[ZRTP_WORD_SIZE]; ///< CRC of ZRTP message +} RelayAckPacket_t; + +#endif // ZRTPPACKET_H + +/** + * @} + */ + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/src/libzrtpcpp/zrtpccrtp.h b/src/libzrtpcpp/zrtpccrtp.h new file mode 100644 index 0000000..d94ca5a --- /dev/null +++ b/src/libzrtpcpp/zrtpccrtp.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _ZRTPCCRTP_H_ +#define _ZRTPCCRTP_H_ + +#include <ccrtp/rtp.h> +#include <libzrtpcpp/ZrtpQueue.h> + +NAMESPACE_COMMONCPP + +// Define a dummy variable only to overcome a doxygen problem. +static int dummy __attribute__ ((unused)) = 0; + + +/** + * @typedef SymmetricZRTPSession + * + * Uses one pair of sockets, (1) for RTP data and (2) for RTCP + * transmission/reception. + * + * This session uses the ZrtpQueue instead of the AVPQueue. The ZrtpQueue + * inherits from AVPQueue and adds support for ZRTP thus enabling + * ad-hoc key negotiation to setup SRTP sessions. + * + * @short Symmetric UDP/IPv4 RTP session scheduled by one thread of execution. + **/ +typedef SingleThreadRTPSession<SymmetricRTPChannel, + SymmetricRTPChannel, + ZrtpQueue> SymmetricZRTPSession; + + +#ifdef CCXX_IPV6 +/** + * @typedef SymmetricZRTPSession + * + * Uses one pair of sockets, (1) for RTP data and (2) for RTCP + * transmission/reception. + * + * This session uses the ZrtpQueue instead of the AVPQueue. The ZrtpQueue + * inherits from AVPQueue and adds support for ZRTP thus enabling + * ad-hoc key negotiation to setup SRTP sessions. + * + * @short Symmetric UDP/IPv6 RTP session scheduled by one thread of execution. + **/ +typedef SingleThreadRTPSessionIPV6<SymmetricRTPChannelIPV6, + SymmetricRTPChannelIPV6, + ZrtpQueue> SymmetricZRTPSessionIPV6; +#endif // CCXX_IPV6 + +END_NAMESPACE + +#endif // _ZRTPCCRTP_H_ + + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ diff --git a/srtp/CryptoContext.cpp b/srtp/CryptoContext.cpp new file mode 100644 index 0000000..d486f86 --- /dev/null +++ b/srtp/CryptoContext.cpp @@ -0,0 +1,465 @@ +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Copyright (C) 2004-2012 + * + * Authors: Israel Abad <i_abad@terra.es> + * Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Joachim Orrblad <joachim@orrblad.com> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <string.h> +#include <arpa/inet.h> +#include <stdio.h> + +#include <CryptoContext.h> +#include <crypto/hmac.h> +#include <crypto/macSkein.h> + +CryptoContext::CryptoContext( uint32_t ssrc, + int32_t roc, + int64_t key_deriv_rate, + const int32_t ealg, + const int32_t aalg, + uint8_t* master_key, + int32_t master_key_length, + uint8_t* master_salt, + int32_t master_salt_length, + int32_t ekeyl, + int32_t akeyl, + int32_t skeyl, + int32_t tagLength): + + ssrcCtx(ssrc),using_mki(false),mkiLength(0),mki(NULL), + roc(roc),guessed_roc(0),s_l(0),key_deriv_rate(key_deriv_rate), + replay_window(0), + master_key_srtp_use_nb(0), master_key_srtcp_use_nb(0), seqNumSet(false), + macCtx(NULL), cipher(NULL), f8Cipher(NULL) +{ + this->ealg = ealg; + this->aalg = aalg; + this->ekeyl = ekeyl; + this->akeyl = akeyl; + this->skeyl = skeyl; + + this->master_key_length = master_key_length; + this->master_key = new uint8_t[master_key_length]; + memcpy(this->master_key, master_key, master_key_length); + + this->master_salt_length = master_salt_length; + this->master_salt = new uint8_t[master_salt_length]; + memcpy(this->master_salt, master_salt, master_salt_length); + + switch (ealg) { + case SrtpEncryptionNull: + n_e = 0; + k_e = NULL; + n_s = 0; + k_s = NULL; + break; + + case SrtpEncryptionTWOF8: + f8Cipher = new SrtpSymCrypto(SrtpEncryptionTWOF8); + + case SrtpEncryptionTWOCM: + n_e = ekeyl; + k_e = new uint8_t[n_e]; + n_s = skeyl; + k_s = new uint8_t[n_s]; + cipher = new SrtpSymCrypto(SrtpEncryptionTWOCM); + break; + + case SrtpEncryptionAESF8: + f8Cipher = new SrtpSymCrypto(SrtpEncryptionAESF8); + + case SrtpEncryptionAESCM: + n_e = ekeyl; + k_e = new uint8_t[n_e]; + n_s = skeyl; + k_s = new uint8_t[n_s]; + cipher = new SrtpSymCrypto(SrtpEncryptionAESCM); + break; + } + + switch (aalg ) { + case SrtpAuthenticationNull: + n_a = 0; + k_a = NULL; + this->tagLength = 0; + break; + + case SrtpAuthenticationSha1Hmac: + case SrtpAuthenticationSkeinHmac: + n_a = akeyl; + k_a = new uint8_t[n_a]; + this->tagLength = tagLength; + break; + } +} + +CryptoContext::~CryptoContext() { + + if (mki) + delete [] mki; + + if (master_key_length > 0) { + memset(master_key, 0, master_key_length); + master_key_length = 0; + delete [] master_key; + } + if (master_salt_length > 0) { + memset(master_salt, 0, master_salt_length); + master_salt_length = 0; + delete [] master_salt; + } + if (n_e > 0) { + memset(k_e, 0, n_e); + n_e = 0; + delete [] k_e; + } + if (n_s > 0) { + memset(k_s, 0, n_s); + n_s = 0; + delete [] k_s; + } + if (n_a > 0) { + memset(k_a, 0, n_a); + n_a = 0; + delete [] k_a; + } + if (cipher != NULL) { + delete cipher; + cipher = NULL; + } + if (f8Cipher != NULL) { + delete f8Cipher; + f8Cipher = NULL; + } + if (macCtx != NULL) { + switch(aalg) { + case SrtpAuthenticationSha1Hmac: + freeSha1HmacContext(macCtx); + break; + + case SrtpAuthenticationSkeinHmac: + freeSkeinMacContext(macCtx); + break; + } + } + ealg = SrtpEncryptionNull; + aalg = SrtpAuthenticationNull; +} + +void CryptoContext::srtpEncrypt(uint8_t* pkt, uint8_t* payload, uint32_t paylen, uint64_t index, uint32_t ssrc ) { + + if (ealg == SrtpEncryptionNull) { + return; + } + if (ealg == SrtpEncryptionAESCM || ealg == SrtpEncryptionTWOCM) { + + /* Compute the CM IV (refer to chapter 4.1.1 in RFC 3711): + * + * k_s XX XX XX XX XX XX XX XX XX XX XX XX XX XX + * SSRC XX XX XX XX + * index XX XX XX XX XX XX + * ------------------------------------------------------XOR + * IV XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 + */ + + unsigned char iv[16]; + memcpy(iv, k_s, 4); + + int i; + for (i = 4; i < 8; i++ ) { + iv[i] = (0xFF & (ssrc >> ((7-i)*8))) ^ k_s[i]; + } + for (i = 8; i < 14; i++ ) { + iv[i] = (0xFF & (unsigned char)( index >> ((13-i)*8) ) ) ^ k_s[i]; + } + iv[14] = iv[15] = 0; + + cipher->ctr_encrypt(payload, paylen, iv); + } + + if (ealg == SrtpEncryptionAESF8 || ealg == SrtpEncryptionTWOF8) { + + /* Create the F8 IV (refer to chapter 4.1.2.2 in RFC 3711): + * + * IV = 0x00 || M || PT || SEQ || TS || SSRC || ROC + * 8Bit 1bit 7bit 16bit 32bit 32bit 32bit + * ------------\ /-------------------------------------------------- + * XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX + */ + + unsigned char iv[16]; + uint32_t *ui32p = (uint32_t *)iv; + + memcpy(iv, pkt, 12); + iv[0] = 0; + + // set ROC in network order into IV + ui32p[3] = htonl(roc); + + cipher->f8_encrypt(payload, paylen, iv, f8Cipher); + } +} + +/* Warning: tag must have been initialized */ +void CryptoContext::srtpAuthenticate(uint8_t* pkt, uint32_t pktlen, uint32_t roc, uint8_t* tag ) +{ + + if (aalg == SrtpAuthenticationNull) { + return; + } + int32_t macL; + + unsigned char temp[20]; + const unsigned char* chunks[3]; + unsigned int chunkLength[3]; + uint32_t beRoc = htonl(roc); + + chunks[0] = pkt; + chunkLength[0] = pktlen; + + chunks[1] = (unsigned char *)&beRoc; + chunkLength[1] = 4; + chunks[2] = NULL; + + switch (aalg) { + case SrtpAuthenticationSha1Hmac: + hmacSha1Ctx(macCtx, + chunks, // data chunks to hash + chunkLength, // length of the data to hash + temp, &macL); + /* truncate the result */ + memcpy(tag, temp, getTagLength()); + break; + case SrtpAuthenticationSkeinHmac: + macSkeinCtx(macCtx, + chunks, // data chunks to hash + chunkLength, // length of the data to hash + temp); + /* truncate the result */ + memcpy(tag, temp, getTagLength()); + break; + } +} + +/* used by the key derivation method */ +static void computeIv(unsigned char* iv, uint64_t label, uint64_t index, + int64_t kdv, unsigned char* master_salt) +{ + + uint64_t key_id; + + if (kdv == 0) { + key_id = label << 48; + } + else { + key_id = ((label << 48) | (index / kdv)); + } + + //printf( "Key_ID: %llx\n", key_id ); + + /* compute the IV + key_id: XX XX XX XX XX XX XX + master_salt: XX XX XX XX XX XX XX XX XX XX XX XX XX XX + ------------------------------------------------------------ XOR + IV: XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 + */ + + int i; + for (i = 0; i < 7 ; i++ ) { + iv[i] = master_salt[i]; + } + + for (i = 7; i < 14 ; i++ ) { + iv[i] = (unsigned char)(0xFF & (key_id >> (8*(13-i)))) ^ + master_salt[i]; + } + + iv[14] = iv[15] = 0; +} + +/* Derive the srtp session keys from the master key */ +void CryptoContext::deriveSrtpKeys(uint64_t index) +{ + uint8_t iv[16]; + + // prepare AES cipher to compute derived keys. + cipher->setNewKey(master_key, master_key_length); + memset(master_key, 0, master_key_length); + + // compute the session encryption key + uint64_t label = 0; + computeIv(iv, label, index, key_deriv_rate, master_salt); + cipher->get_ctr_cipher_stream(k_e, n_e, iv); + + // compute the session authentication key + label = 0x01; + computeIv(iv, label, index, key_deriv_rate, master_salt); + cipher->get_ctr_cipher_stream(k_a, n_a, iv); + + // Initialize MAC context with the derived key + switch (aalg) { + case SrtpAuthenticationSha1Hmac: + macCtx = createSha1HmacContext(k_a, n_a); + break; + case SrtpAuthenticationSkeinHmac: + // Skein MAC uses number of bits as MAC size, not just bytes + macCtx = createSkeinMacContext(k_a, n_a, tagLength*8, Skein512); + break; + } + memset(k_a, 0, n_a); + + // compute the session salt + label = 0x02; + computeIv(iv, label, index, key_deriv_rate, master_salt); + cipher->get_ctr_cipher_stream(k_s, n_s, iv); + memset(master_salt, 0, master_salt_length); + + // as last step prepare AES cipher with derived key. + cipher->setNewKey(k_e, n_e); + if (f8Cipher != NULL) + cipher->f8_deriveForIV(f8Cipher, k_e, n_e, k_s, n_s); + memset(k_e, 0, n_e); +} + +/* Based on the algorithm provided in Appendix A - draft-ietf-srtp-05.txt */ +uint64_t CryptoContext::guessIndex(uint16_t new_seq_nb ) +{ + /* + * Initialize the sequences number on first call that uses the + * sequence number. Either GuessIndex() or checkReplay(). + */ + if (!seqNumSet) { + seqNumSet = true; + s_l = new_seq_nb; + } + if (s_l < 32768) { + if (new_seq_nb - s_l > 32768) { + guessed_roc = roc - 1; + } + else { + guessed_roc = roc; + } + } + else { + if (s_l - 32768 > new_seq_nb) { + guessed_roc = roc + 1; + } + else { + guessed_roc = roc; + } + } + + return ((uint64_t)guessed_roc) << 16 | new_seq_nb; +} + +bool CryptoContext::checkReplay( uint16_t new_seq_nb ) +{ + if ( aalg == SrtpAuthenticationNull && ealg == SrtpEncryptionNull ) { + /* No security policy, don't use the replay protection */ + return true; + } + + /* + * Initialize the sequences number on first call that uses the + * sequence number. Either guessIndex() or checkReplay(). + */ + if (!seqNumSet) { + seqNumSet = true; + s_l = new_seq_nb; + } + uint64_t guessed_index = guessIndex( new_seq_nb ); + uint64_t local_index = (((uint64_t)roc) << 16) | s_l; + + int64_t delta = guessed_index - local_index; + if (delta > 0) { + /* Packet not yet received*/ + return true; + } + else { + if ( -delta > REPLAY_WINDOW_SIZE ) { + /* Packet too old */ + return false; + } + else { + if ((replay_window >> (-delta)) & 0x1) { + /* Packet already received ! */ + return false; + } + else { + /* Packet not yet received */ + return true; + } + } + } +} + +void CryptoContext::update(uint16_t new_seq_nb) +{ + int64_t delta = guessIndex(new_seq_nb) - (((uint64_t)roc) << 16 | s_l ); + + /* update the replay bitmask */ + if ( delta > 0 ) { + replay_window = replay_window << delta; + replay_window |= 1; + } + else { + replay_window |= ( 1 << delta ); + } + + /* update the locally stored ROC and highest sequence number */ + if ( new_seq_nb > s_l ) { + s_l = new_seq_nb; + } + if ( guessed_roc > roc ) { + roc = guessed_roc; + s_l = new_seq_nb; + } +} + +CryptoContext* CryptoContext::newCryptoContextForSSRC(uint32_t ssrc, int roc, int64_t keyDerivRate) +{ + CryptoContext* pcc = new CryptoContext( + ssrc, + roc, // Roll over Counter, + keyDerivRate, // keyderivation << 48, + this->ealg, // encryption algo + this->aalg, // authentication algo + this->master_key, // Master Key + this->master_key_length, // Master Key length + this->master_salt, // Master Salt + this->master_salt_length, // Master Salt length + this->ekeyl, // encryption keyl + this->akeyl, // authentication key len + this->skeyl, // session salt len + this->tagLength); // authentication tag len + + return pcc; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/srtp/CryptoContext.h b/srtp/CryptoContext.h new file mode 100644 index 0000000..3075bf5 --- /dev/null +++ b/srtp/CryptoContext.h @@ -0,0 +1,418 @@ +/* + Copyright (C) 2004-2006 the Minisip Team + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#ifndef CRYPTOCONTEXT_H +#define CRYPTOCONTEXT_H + +/** + * @file CryptoContext.h + * @brief The C++ SRTP implementation + * @ingroup Z_SRTP + * @{ + */ + +#define REPLAY_WINDOW_SIZE 64 + +const int SrtpAuthenticationNull = 0; +const int SrtpAuthenticationSha1Hmac = 1; +const int SrtpAuthenticationSkeinHmac = 2; + +const int SrtpEncryptionNull = 0; +const int SrtpEncryptionAESCM = 1; +const int SrtpEncryptionAESF8 = 2; +const int SrtpEncryptionTWOCM = 3; +const int SrtpEncryptionTWOF8 = 4; + +#ifndef CRYPTOCONTEXTCTRL_H + +#include <stdint.h> +#include <crypto/SrtpSymCrypto.h> + +class SrtpSymCrypto; + +/** + * The implementation for a SRTP cryptographic context. + * + * This class holds data and provides functions that implement a + * cryptographic context for SRTP, Refer to RFC 3711, chapter 3.2 for some + * more detailed information about the SRTP cryptographic context. + * + * Each SRTP cryptographic context maintains a RTP source identified by + * its SSRC. Thus you can independently protect each source inside a RTP + * session. + * + * Key management mechanisms negotiate the parameters for the SRTP + * cryptographic context, such as master key, key length, authentication + * length and so on. The key management mechanisms are not part of + * SRTP. Refer to MIKEY (RFC 3880) or to Phil Zimmermann's ZRTP protocol + * (draft-zimmermann-avt-zrtp-01). After key management negotiated the + * data the application can setup the SRTP cryptographic context and + * enable SRTP processing. + * + * Currently this implementation supports RTP only, not RTCP. + * + * @author Israel Abad <i_abad@terra.es> + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Joachim Orrblad <joachim@orrblad.com> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class CryptoContext { +public: + /** + * Constructor for an active SRTP cryptographic context. + * + * This constructor creates an active SRTP cryptographic context were + * algorithms are enabled, keys are computed and so on. This SRTP + * cryptographic context can protect a RTP SSRC stream. + * + * @param ssrc + * The RTP SSRC that this SRTP cryptographic context protects. + * + * @param roc + * The initial Roll-Over-Counter according to RFC 3711. These are the + * upper 32 bit of the overall 48 bit SRTP packet index. Refer to + * chapter 3.2.1 of the RFC. + * + * @param keyDerivRate + * The key derivation rate defines when to recompute the SRTP session + * keys. Refer to chapter 4.3.1 in the RFC. + * + * @param ealg + * The encryption algorithm to use. Possible values are <code> + * SrtpEncryptionNull, SrtpEncryptionAESCM, SrtpEncryptionAESF8 + * </code>. See chapter 4.1.1 for AESCM (Counter mode) and 4.1.2 + * for AES F8 mode. + * + * @param aalg + * The authentication algorithm to use. Possible values are <code> + * SrtpEncryptionNull, SrtpAuthenticationSha1Hmac</code>. The only + * active algorithm here is SHA1 HMAC, a SHA1 based hashed message + * authentication code as defined in RFC 2104. + * + * @param masterKey + * Pointer to the master key for this SRTP cryptographic context. + * Must point to <code>masterKeyLength</code> bytes. Refer to chapter + * 3.2.1 of the RFC about the role of the master key. + * + * @param masterKeyLength + * The length in bytes of the master key in bytes. The length must + * match the selected encryption algorithm. Because SRTP uses AES + * based encryption only, then master key length may be 16 or 32 + * bytes (128 or 256 bit master key) + * + * @param masterSalt + * SRTP uses the master salt to computer the initialization vector + * that in turn is input to compute the session key, session + * authentication key and the session salt. + * + * @param masterSaltLength + * The length in bytes of the master salt data in bytes. SRTP uses + * AES as encryption algorithm. AES encrypts 16 byte blocks + * (independent of the key length). According to RFC3711 the standard + * value for the master salt length should be 112 bit (14 bytes). + * + * @param ekeyl + * The length in bytes of the session encryption key that SRTP shall + * compute and use. Usually the same length as for the master key + * length. But you may use a different length as well. Be carefull + * that the key management mechanisms supports different key lengths. + * + * @param akeyl + * The length in bytes of the session authentication key. SRTP + * computes this key and uses it as input to the authentication + * algorithm. + * The standard value is 160 bits (20 bytes). + * + * @param skeyl + * The length in bytes of the session salt. SRTP computes this salt + * key and uses it as input during encryption. The length usually + * is the same as the master salt length. + * + * @param tagLength + * The length is bytes of the authentication tag that SRTP appends + * to the RTP packet. Refer to chapter 4.2. in the RFC 3711. + */ + CryptoContext( uint32_t ssrc, int32_t roc, + int64_t keyDerivRate, + const int32_t ealg, + const int32_t aalg, + uint8_t* masterKey, + int32_t masterKeyLength, + uint8_t* masterSalt, + int32_t masterSaltLength, + int32_t ekeyl, + int32_t akeyl, + int32_t skeyl, + int32_t tagLength ); + /** + * Destructor. + * + * Cleans the SRTP cryptographic context. + */ + ~CryptoContext(); + + /** + * Set the Roll-Over-Counter. + * + * Ths method sets the upper 32 bit of the 48 bit SRTP packet index + * (the roll-over-part) + * + * @param r + * The roll-over-counter + */ + inline void + setRoc(uint32_t r) + { + roc = r; + } + + /** + * Get the Roll-Over-Counter. + * + * Ths method get the upper 32 bit of the 48 bit SRTP packet index + * (the roll-over-part) + * + * @return The roll-over-counter + */ + inline uint32_t + getRoc() const + { + return roc; + } + + /** + * Perform SRTP encryption. + * + * This method encrypts <em>and</em> decrypts SRTP payload data. Plain + * data gets encrypted, encrypted data get decrypted. + * + * @param pkt + * Pointer to RTP packet buffer, used for F8. + * + * @param payload + * The data to encrypt. + * + * @param paylen + * Length of payload. + * + * @param index + * The 48 bit SRTP packet index. See the <code>guessIndex</code> + * method. + * + * @param ssrc + * The RTP SSRC data in <em>host</em> order. + */ + void srtpEncrypt(uint8_t* pkt, uint8_t* payload, uint32_t paylen, uint64_t index, uint32_t ssrc ); + + /** + * Compute the authentication tag. + * + * Compute the authentication tag according the the paramters in the + * SRTP Cryptograhic context. + * + * @param pkt + * Pointer to RTP packet buffer that contains the data to authenticate. + * + * @param pktlen + * Length of the RTP packet buffer + * + * @param roc + * The 32 bit SRTP roll-over-counter. + * + * @param tag + * Points to a buffer that hold the computed tag. This buffer must + * be able to hold <code>tagLength</code> bytes. + */ + void srtpAuthenticate(uint8_t* pkt, uint32_t pktlen, uint32_t roc, uint8_t* tag ); + + /** + * Perform key derivation according to SRTP specification + * + * This method computes the session key, session authentication key and the + * session salt key. This method must be called at least once after the + * SRTP Cryptograhic context was set up. + * + * @param index + * The 48 bit SRTP packet index. See the <code>guessIndex</code> + * method. + */ + void deriveSrtpKeys(uint64_t index); + + /** + * Compute (guess) the new SRTP index based on the sequence number of + * a received RTP packet. + * + * The method uses the algorithm show in RFC3711, Appendix A, to compute + * the new index. + * + * @param newSeqNumber + * The sequence number of the received RTP packet in host order. + * + * @return The new SRTP packet index + */ + uint64_t guessIndex(uint16_t newSeqNumber); + + /** + * Check for packet replay. + * + * The method check if a received packet is either to old or was already + * received. + * + * The method supports a 64 packet history relative the the given + * sequence number. + * + * @param newSeqNumber + * The sequence number of the received RTP packet in host order. + * + * @return <code>true</code> if no replay, <code>false</code> if packet + * is too old ar was already received. + */ + bool checkReplay(uint16_t newSeqNumber); + + /** + * Update the SRTP packet index. + * + * Call this method after all checks were successful. See chapter + * 3.3.1 in the RFC when to update the ROC and ROC processing. + * + * @param newSeqNumber + * The sequence number of the received RTP packet in host order. + */ + void update( uint16_t newSeqNumber ); + + /** + * Get the length of the SRTP authentication tag in bytes. + * + * @return the length of the authentication tag. + */ + inline int32_t + getTagLength() const + { + return tagLength; + } + + + /** + * Get the length of the MKI in bytes. + * + * @return the length of the MKI. + */ + inline int32_t + getMkiLength() const + { + return mkiLength; + } + + /** + * Get the SSRC of this SRTP Cryptograhic context. + * + * @return the SSRC. + */ + inline uint32_t + getSsrc() const + { + return ssrcCtx; + } + + /** + * Derive a new Crypto Context for use with a new SSRC + * + * This method returns a new Crypto Context initialized with the data + * of this crypto context. Replacing the SSRC, Roll-over-Counter, and + * the key derivation rate the application cab use this Crypto Context + * to encrypt / decrypt a new stream (Synchronization source) inside + * one RTP session. + * + * Before the application can use this crypto context it must call + * the <code>deriveSrtpKeys</code> method. + * + * @param ssrc + * The SSRC for this context + * @param roc + * The Roll-Over-Counter for this context + * @param keyDerivRate + * The key derivation rate for this context + * @return + * a new CryptoContext with all relevant data set. + */ + + CryptoContext* newCryptoContextForSSRC(uint32_t ssrc, int roc, int64_t keyDerivRate); + +private: + + uint32_t ssrcCtx; + bool using_mki; + uint32_t mkiLength; + uint8_t* mki; + + uint32_t roc; + uint32_t guessed_roc; + uint16_t s_l; + int64_t key_deriv_rate; + + /* bitmask for replay check */ + uint64_t replay_window; + + uint8_t* master_key; + uint32_t master_key_length; + uint32_t master_key_srtp_use_nb; + uint32_t master_key_srtcp_use_nb; + uint8_t* master_salt; + uint32_t master_salt_length; + + /* Session Encryption, Authentication keys, Salt */ + int32_t n_e; + uint8_t* k_e; + int32_t n_a; + uint8_t* k_a; + int32_t n_s; + uint8_t* k_s; + + int32_t ealg; + int32_t aalg; + int32_t ekeyl; + int32_t akeyl; + int32_t skeyl; + int32_t tagLength; + bool seqNumSet; + + void* macCtx; + + SrtpSymCrypto* cipher; + SrtpSymCrypto* f8Cipher; +}; + +#endif + +/** + * @} + */ +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/srtp/CryptoContextCtrl.cpp b/srtp/CryptoContextCtrl.cpp new file mode 100644 index 0000000..caf5746 --- /dev/null +++ b/srtp/CryptoContextCtrl.cpp @@ -0,0 +1,408 @@ +/* + Copyright (C) 2004-2006 the Minisip Team + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Copyright (C) 2004-2012 + * + * Authors: Israel Abad <i_abad@terra.es> + * Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Joachim Orrblad <joachim@orrblad.com> + * Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#include <string.h> +#include <arpa/inet.h> +#include <stdio.h> + +#include <CryptoContextCtrl.h> +#include <CryptoContext.h> + +#include <crypto/hmac.h> +#include <crypto/macSkein.h> + + +CryptoContextCtrl::CryptoContextCtrl(uint32_t ssrc, + const int32_t ealg, + const int32_t aalg, + uint8_t* master_key, + int32_t master_key_length, + uint8_t* master_salt, + int32_t master_salt_length, + int32_t ekeyl, + int32_t akeyl, + int32_t skeyl, + int32_t tagLength): +ssrcCtx(ssrc),using_mki(false),mkiLength(0),mki(NULL), +replay_window(0), macCtx(NULL), cipher(NULL), f8Cipher(NULL) +{ + this->ealg = ealg; + this->aalg = aalg; + this->ekeyl = ekeyl; + this->akeyl = akeyl; + this->skeyl = skeyl; + + this->master_key_length = master_key_length; + this->master_key = new uint8_t[master_key_length]; + memcpy(this->master_key, master_key, master_key_length); + + this->master_salt_length = master_salt_length; + this->master_salt = new uint8_t[master_salt_length]; + memcpy(this->master_salt, master_salt, master_salt_length); + + switch (ealg) { + case SrtpEncryptionNull: + n_e = 0; + k_e = NULL; + n_s = 0; + k_s = NULL; + break; + + case SrtpEncryptionTWOF8: + f8Cipher = new SrtpSymCrypto(SrtpEncryptionTWOF8); + + case SrtpEncryptionTWOCM: + n_e = ekeyl; + k_e = new uint8_t[n_e]; + n_s = skeyl; + k_s = new uint8_t[n_s]; + cipher = new SrtpSymCrypto(SrtpEncryptionTWOCM); + break; + + case SrtpEncryptionAESF8: + f8Cipher = new SrtpSymCrypto(SrtpEncryptionAESF8); + + case SrtpEncryptionAESCM: + n_e = ekeyl; + k_e = new uint8_t[n_e]; + n_s = skeyl; + k_s = new uint8_t[n_s]; + cipher = new SrtpSymCrypto(SrtpEncryptionAESCM); + break; + } + + switch (aalg) { + case SrtpAuthenticationNull: + n_a = 0; + k_a = NULL; + this->tagLength = 0; + break; + + case SrtpAuthenticationSha1Hmac: + case SrtpAuthenticationSkeinHmac: + n_a = akeyl; + k_a = new uint8_t[n_a]; + this->tagLength = tagLength; + break; + } +} + +CryptoContextCtrl::~CryptoContextCtrl(){ + + if (mki) + delete [] mki; + + if (master_key_length > 0) { + memset(master_key, 0, master_key_length); + master_key_length = 0; + delete [] master_key; + } + if (master_salt_length > 0) { + memset(master_salt, 0, master_salt_length); + master_salt_length = 0; + delete [] master_salt; + } + if (n_e > 0) { + memset(k_e, 0, n_e); + n_e = 0; + delete [] k_e; + } + if (n_s > 0) { + memset(k_s, 0, n_s); + n_s = 0; + delete [] k_s; + } + if (n_a > 0) { + n_a = 0; + memset(k_a, 0, n_a); + delete [] k_a; + } + if (cipher != NULL) { + delete cipher; + cipher = NULL; + } + if (f8Cipher != NULL) { + delete f8Cipher; + f8Cipher = NULL; + } + if (macCtx != NULL) { + switch(aalg) { + case SrtpAuthenticationSha1Hmac: + freeSha1HmacContext(macCtx); + break; + + case SrtpAuthenticationSkeinHmac: + freeSkeinMacContext(macCtx); + break; + } + } + ealg = SrtpEncryptionNull; + aalg = SrtpAuthenticationNull; +} + +void CryptoContextCtrl::srtcpEncrypt( uint8_t* rtp, int32_t len, uint64_t index, uint32_t ssrc ) +{ + if (ealg == SrtpEncryptionNull) { + return; + } + if (ealg == SrtpEncryptionAESCM || ealg == SrtpEncryptionTWOCM) { + + /* Compute the CM IV (refer to chapter 4.1.1 in RFC 3711): + * + * k_s XX XX XX XX XX XX XX XX XX XX XX XX XX XX + * SSRC XX XX XX XX + * index XX XX XX XX + * ------------------------------------------------------XOR + * IV XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + */ + unsigned char iv[16]; + + iv[0] = k_s[0]; + iv[1] = k_s[1]; + iv[2] = k_s[2]; + iv[3] = k_s[3]; + + // The shifts transform the ssrc and index into network order + iv[4] = ((ssrc >> 24) & 0xff) ^ k_s[4]; + iv[5] = ((ssrc >> 16) & 0xff) ^ k_s[5]; + iv[6] = ((ssrc >> 8) & 0xff) ^ k_s[6]; + iv[7] = (ssrc & 0xff) ^ k_s[7]; + + iv[8] = k_s[8]; + iv[9] = k_s[9]; + + iv[10] = ((index >> 24) & 0xff) ^ k_s[10]; + iv[11] = ((index >> 16) & 0xff) ^ k_s[11]; + iv[12] = ((index >> 8) & 0xff) ^ k_s[12]; + iv[13] = (index & 0xff) ^ k_s[13]; + + iv[14] = iv[15] = 0; + + cipher->ctr_encrypt(rtp, len, iv); + } + + if (ealg == SrtpEncryptionAESF8 || ealg == SrtpEncryptionTWOF8) { + + unsigned char iv[16]; + + // 4 bytes of the iv are zero + // the first byte of the RTP header is not used. + iv[0] = 0; + iv[1] = 0; + iv[2] = 0; + iv[3] = 0; + + // Need the encryption flag + index = index | 0x80000000; + + // set the index and the encrypt flag in network order into IV + iv[4] = index >> 24; + iv[5] = index >> 16; + iv[6] = index >> 8; + iv[7] = index; + + // The fixed header follows and fills the rest of the IV + memcpy(iv+8, rtp, 8); + + cipher->f8_encrypt(rtp, len, iv, f8Cipher); + } +} + +/* Warning: tag must have been initialized */ +void CryptoContextCtrl::srtcpAuthenticate(uint8_t* rtp, int32_t len, uint32_t index, uint8_t* tag ) +{ + if (aalg == SrtpAuthenticationNull) { + return; + } + int32_t macL; + + unsigned char temp[20]; + const unsigned char* chunks[3]; + unsigned int chunkLength[3]; + uint32_t beIndex = htonl(index); + + chunks[0] = rtp; + chunkLength[0] = len; + + chunks[1] = (unsigned char *)&beIndex; + chunkLength[1] = 4; + chunks[2] = NULL; + + switch (aalg) { + case SrtpAuthenticationSha1Hmac: + hmacSha1Ctx(macCtx, + chunks, // data chunks to hash + chunkLength, // length of the data to hash + temp, &macL); + /* truncate the result */ + memcpy(tag, temp, getTagLength()); + break; + case SrtpAuthenticationSkeinHmac: + macSkeinCtx(macCtx, + chunks, // data chunks to hash + chunkLength, // length of the data to hash + temp); + /* truncate the result */ + memcpy(tag, temp, getTagLength()); + break; + } +} + +/* used by the key derivation method */ +static void computeIv(unsigned char* iv, uint8_t label, uint8_t* master_salt) +{ + //printf( "Key_ID: %llx\n", key_id ); + + /* compute the IV + key_id: XX XX XX XX XX XX XX + master_salt: XX XX XX XX XX XX XX XX XX XX XX XX XX XX + ------------------------------------------------------------ XOR + IV: XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 + */ + + memcpy(iv, master_salt, 14); + iv[7] ^= label; + + iv[14] = iv[15] = 0; +} + +/* Derives the srtp session keys from the master key */ +void CryptoContextCtrl::deriveSrtcpKeys() +{ + uint8_t iv[16]; + + // prepare AES cipher to compute derived keys. + cipher->setNewKey(master_key, master_key_length); + memset(master_key, 0, master_key_length); + + // compute the session encryption key + uint8_t label = 3; + computeIv(iv, label, master_salt); + cipher->get_ctr_cipher_stream(k_e, n_e, iv); + + // compute the session authentication key + label = 4; + computeIv(iv, label, master_salt); + cipher->get_ctr_cipher_stream(k_a, n_a, iv); + + // Initialize MAC context with the derived key + switch (aalg) { + case SrtpAuthenticationSha1Hmac: + macCtx = createSha1HmacContext(k_a, n_a); + break; + case SrtpAuthenticationSkeinHmac: + // Skein MAC uses number of bits as MAC size, not just bytes + macCtx = createSkeinMacContext(k_a, n_a, tagLength*8, Skein512); + break; + } + memset(k_a, 0, n_a); + + // compute the session salt + label = 5; + computeIv(iv, label, master_salt); + cipher->get_ctr_cipher_stream(k_s, n_s, iv); + memset(master_salt, 0, master_salt_length); + + // as last step prepare AES cipher with derived key. + cipher->setNewKey(k_e, n_e); + if (f8Cipher != NULL) + cipher->f8_deriveForIV(f8Cipher, k_e, n_e, k_s, n_s); + memset(k_e, 0, n_e); +} + +bool CryptoContextCtrl::checkReplay( uint32_t index ) +{ + if ( aalg == SrtpAuthenticationNull && ealg == SrtpEncryptionNull ) { + /* No security policy, don't use the replay protection */ + return true; + } + + int64_t delta = s_l - index; + if (delta > 0) { + /* Packet not yet received*/ + return true; + } + else { + if( -delta > REPLAY_WINDOW_SIZE ) { + /* Packet too old */ + return false; + } + else { + if((replay_window >> (-delta)) & 0x1) { + /* Packet already received ! */ + return false; + } + else { + /* Packet not yet received */ + return true; + } + } + } +} + +void CryptoContextCtrl::update(uint32_t index) +{ + int64_t delta = index - s_l; + + /* update the replay bitmask */ + if( delta > 0 ){ + replay_window = replay_window << delta; + replay_window |= 1; + } + else { + replay_window |= ( 1 << delta ); + } + s_l = index; +} + +CryptoContextCtrl* CryptoContextCtrl::newCryptoContextForSSRC(uint32_t ssrc) +{ + CryptoContextCtrl* pcc = new CryptoContextCtrl( + ssrc, + this->ealg, // encryption algo + this->aalg, // authentication algo + this->master_key, // Master Key + this->master_key_length, // Master Key length + this->master_salt, // Master Salt + this->master_salt_length, // Master Salt length + this->ekeyl, // encryption keyl + this->akeyl, // authentication key len + this->skeyl, // session salt len + this->tagLength); // authentication tag len + + return pcc; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/srtp/CryptoContextCtrl.h b/srtp/CryptoContextCtrl.h new file mode 100644 index 0000000..456e58f --- /dev/null +++ b/srtp/CryptoContextCtrl.h @@ -0,0 +1,327 @@ +/* + Copyright (C) 2004-2006 the Minisip Team + Copyright (C) 2011 Werner Dittmann for the SRTCP support + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#ifndef CRYPTOCONTEXTCTRL_H +#define CRYPTOCONTEXTCTRL_H + +/** + * @file CryptoContext.h + * @brief The C++ SRTP implementation + * @ingroup Z_SRTP + * @{ + */ + +#include <crypto/SrtpSymCrypto.h> + +class SrtpSymCrypto; + + /** + * The implementation for a SRTCP cryptographic context. + * + * This class holds data and provides functions that implement a + * cryptographic context for SRTP, Refer to RFC 3711, chapter 3.2 for some + * more detailed information about the SRTP cryptographic context. + * + * Each SRTP cryptographic context maintains a RTP source identified by + * its SSRC. Thus you can independently protect each source inside a RTP + * session. + * + * Key management mechanisms negotiate the parameters for the SRTP + * cryptographic context, such as master key, key length, authentication + * length and so on. The key management mechanisms are not part of + * SRTP. Refer to MIKEY (RFC 3880) or to Phil Zimmermann's ZRTP protocol + * (draft-zimmermann-avt-zrtp-01). After key management negotiated the + * data the application can setup the SRTCP cryptographic context and + * enable SRTCP processing. + * + * + * @author Israel Abad <i_abad@terra.es> + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Joachim Orrblad <joachim@orrblad.com> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +class CryptoContextCtrl { + public: + /** + * Constructor for an active SRTP cryptographic context. + * + * This constructor creates an active SRTP cryptographic context were + * algorithms are enabled, keys are computed and so on. This SRTP + * cryptographic context can protect a RTP SSRC stream. + * + * @param ssrc + * The RTP SSRC that this SRTP cryptographic context protects. + * + * @param ealg + * The encryption algorithm to use. Possible values are <code> + * SrtpEncryptionNull, SrtpEncryptionAESCM, SrtpEncryptionAESF8 + * </code>. See chapter 4.1.1 for AESCM (Counter mode) and 4.1.2 + * for AES F8 mode. + * + * @param aalg + * The authentication algorithm to use. Possible values are <code> + * SrtpEncryptionNull, SrtpAuthenticationSha1Hmac</code>. The only + * active algorithm here is SHA1 HMAC, a SHA1 based hashed message + * authentication code as defined in RFC 2104. + * + * @param masterKey + * Pointer to the master key for this SRTP cryptographic context. + * Must point to <code>masterKeyLength</code> bytes. Refer to chapter + * 3.2.1 of the RFC about the role of the master key. + * + * @param masterKeyLength + * The length in bytes of the master key in bytes. The length must + * match the selected encryption algorithm. Because SRTP uses AES + * based encryption only, then master key length may be 16 or 32 + * bytes (128 or 256 bit master key) + * + * @param masterSalt + * SRTP uses the master salt to computer the initialization vector + * that in turn is input to compute the session key, session + * authentication key and the session salt. + * + * @param masterSaltLength + * The length in bytes of the master salt data in bytes. SRTP uses + * AES as encryption algorithm. AES encrypts 16 byte blocks + * (independent of the key length). According to RFC3711 the standard + * value for the master salt length should be 112 bit (14 bytes). + * + * @param ekeyl + * The length in bytes of the session encryption key that SRTP shall + * compute and use. Usually the same length as for the master key + * length. But you may use a different length as well. Be carefull + * that the key management mechanisms supports different key lengths. + * + * @param akeyl + * The length in bytes of the session authentication key. SRTP + * computes this key and uses it as input to the authentication + * algorithm. + * The standard value is 160 bits (20 bytes). + * + * @param skeyl + * The length in bytes of the session salt. SRTP computes this salt + * key and uses it as input during encryption. The length usually + * is the same as the master salt length. + * + * @param tagLength + * The length is bytes of the authentication tag that SRTP appends + * to the RTP packet. Refer to chapter 4.2. in the RFC 3711. + */ + CryptoContextCtrl( uint32_t ssrc, + const int32_t ealg, + const int32_t aalg, + uint8_t* masterKey, + int32_t masterKeyLength, + uint8_t* masterSalt, + int32_t masterSaltLength, + int32_t ekeyl, + int32_t akeyl, + int32_t skeyl, + int32_t tagLength ); + /** + * Destructor. + * + * Cleans the SRTP cryptographic context. + */ + ~CryptoContextCtrl(); + + /** + * Perform SRTP encryption. + * + * This method encrypts <em>and</em> decrypts SRTP payload data. Plain + * data gets encrypted, encrypted data get decrypted. + * + * @param rtp + * The RTP packet that contains the data to encrypt. + * + * @param index + * The 48 bit SRTP packet index. See the <code>guessIndex</code> + * method. + * + * @param ssrc + * The RTP SSRC data in <em>host</em> order. + */ + void srtcpEncrypt( uint8_t* rtp, int32_t len, uint64_t index, uint32_t ssrc ); + + /** + * Compute the authentication tag. + * + * Compute the authentication tag according the the paramters in the + * SRTP Cryptograhic context. + * + * @param rtp + * The RTP packet that contains the data to authenticate. + * + * @param roc + * The 32 bit SRTP roll-over-counter. + * + * @param tag + * Points to a buffer that hold the computed tag. This buffer must + * be able to hold <code>tagLength</code> bytes. + */ + void srtcpAuthenticate(uint8_t* rtp, int32_t len, uint32_t roc, uint8_t* tag ); + + /** + * Perform key derivation according to SRTP specification + * + * This method computes the session key, session authentication key and the + * session salt key. This method must be called at least once after the + * SRTP Cryptograhic context was set up. + * + * @param index + * The 48 bit SRTP packet index. See the <code>guessIndex</code> + * method. + */ + void deriveSrtcpKeys(); + + /** + * Check for packet replay. + * + * The method check if a received packet is either to old or was already + * received. + * + * The method supports a 64 packet history relative the the given + * sequence number. + * + * @param newSeqNumber + * The sequence number of the received RTCP packet in host order. + * + * @return <code>true</code> if no replay, <code>false</code> if packet + * is too old ar was already received. + */ + bool checkReplay(uint32_t newSeqNumber); + + /** + * Update the SRTP packet index. + * + * Call this method after all checks were successful. See chapter + * 3.3.1 in the RFC when to update the ROC and ROC processing. + * + * @param newSeqNumber + * The sequence number of the received RTCP packet in host order. + */ + void update( uint32_t newSeqNumber ); + + /** + * Get the length of the SRTP authentication tag in bytes. + * + * @return the length of the authentication tag. + */ + inline int32_t + getTagLength() const + {return tagLength;} + + + /** + * Get the length of the MKI in bytes. + * + * @return the length of the MKI. + */ + inline int32_t + getMkiLength() const + {return mkiLength;} + + /** + * Get the SSRC of this SRTP Cryptograhic context. + * + * @return the SSRC. + */ + inline uint32_t + getSsrc() const + {return ssrcCtx;} + + /** + * Derive a new Crypto Context for use with a new SSRC + * + * This method returns a new Crypto Context initialized with the data + * of this crypto context. Replacing the SSRC, Roll-over-Counter, and + * the key derivation rate the application cab use this Crypto Context + * to encrypt / decrypt a new stream (Synchronization source) inside + * one RTP session. + * + * Before the application can use this crypto context it must call + * the <code>deriveSrtpKeys</code> method. + * + * @param ssrc + * The SSRC for this context + * @param roc + * The Roll-Over-Counter for this context + * @param keyDerivRate + * The key derivation rate for this context + * @return + * a new CryptoContext with all relevant data set. + */ + CryptoContextCtrl* newCryptoContextForSSRC(uint32_t ssrc); + + private: + + uint32_t ssrcCtx; + bool using_mki; + uint32_t mkiLength; + uint8_t* mki; + + uint32_t s_l; + + /* bitmask for replay check */ + uint64_t replay_window; + + uint8_t* master_key; + uint32_t master_key_length; + uint8_t* master_salt; + uint32_t master_salt_length; + + /* Session Encryption, Authentication keys, Salt */ + int32_t n_e; + uint8_t* k_e; + int32_t n_a; + uint8_t* k_a; + int32_t n_s; + uint8_t* k_s; + + int32_t ealg; + int32_t aalg; + int32_t ekeyl; + int32_t akeyl; + int32_t skeyl; + int32_t tagLength; + + void* macCtx; + + SrtpSymCrypto* cipher; + SrtpSymCrypto* f8Cipher; + }; + +/** + * @} + */ + +#endif + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/srtp/crypto/SrtpSymCrypto.h b/srtp/crypto/SrtpSymCrypto.h new file mode 100644 index 0000000..1b596c8 --- /dev/null +++ b/srtp/crypto/SrtpSymCrypto.h @@ -0,0 +1,393 @@ +/*
+ Copyright (C) 2005, 2004, 2010, 2012 Erik Eliasson, Johan Bilien, Werner Dittmann
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+*/
+
+
+
+#ifndef SRTPSYMCRYPTO_H
+#define SRTPSYMCRYPTO_H
+
+/**
+ * @file SrtpSymCrypto.h
+ * @brief Class which implements SRTP AES cryptographic functions
+ *
+ * @ingroup GNU_ZRTP
+ * @{
+ */
+
+#include <stdint.h>
+#include <CryptoContext.h>
+
+#ifndef SRTP_BLOCK_SIZE
+#define SRTP_BLOCK_SIZE 16
+#endif
+
+typedef struct _f8_ctx {
+ unsigned char *S; ///< Intermetiade buffer
+ unsigned char *ivAccent; ///< second IV
+ uint32_t J; ///< Counter
+} F8_CIPHER_CTX;
+
+/**
+ * Implments the SRTP encryption modes as defined in RFC3711
+ *
+ * The SRTP specification defines two encryption modes, AES-CTR
+ * (AES Counter mode) and AES-F8 mode. The AES-CTR is required,
+ * AES-F8 is optional.
+ *
+ * Both modes are desinged to encrypt/decrypt data of arbitrary length
+ * (with a specified upper limit, refer to RFC 3711). These modes do
+ * <em>not</em> require that the amount of data to encrypt is a multiple
+ * of the AES blocksize (16 bytes), no padding is necessary.
+ *
+ * The implementation uses the openSSL library as its cryptographic
+ * backend.
+ *
+ * @author Erik Eliasson <eliasson@it.kth.se>
+ * @author Johan Bilien <jobi@via.ecp.fr>
+ * @author Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+class SrtpSymCrypto {
+public:
+ SrtpSymCrypto(int algo = SrtpEncryptionAESCM);
+
+ /**
+ * Constructor that initializes key data
+ *
+ * @param key
+ * Pointer to key bytes.
+ * @param key_length
+ * Number of key bytes.
+ */
+ SrtpSymCrypto(uint8_t* key, int32_t key_length, int algo = SrtpEncryptionAESCM);
+
+ ~SrtpSymCrypto();
+
+ /**
+ * Encrypts the inpout to the output.
+ *
+ * Encrypts one input block to one output block. Each block
+ * is 16 bytes according to the AES encryption algorithm used.
+ *
+ * @param input
+ * Pointer to input block, must be 16 bytes
+ *
+ * @param output
+ * Pointer to output block, must be 16 bytes
+ */
+ void encrypt( const uint8_t* input, uint8_t* output );
+
+ /**
+ * Set new key
+ *
+ * @param key
+ * Pointer to key data, must have at least a size of keyLength
+ *
+ * @param keyLength
+ * Length of the key in bytes, must be 16, 24, or 32
+ *
+ * @return
+ * false if key could not set.
+ */
+ bool setNewKey(const uint8_t* key, int32_t keyLength);
+
+ /**
+ * Computes the cipher stream for AES CM mode.
+ *
+ * @param output
+ * Pointer to a buffer that receives the cipher stream. Must be
+ * at least <code>length</code> bytes long.
+ *
+ * @param length
+ * Number of cipher stream bytes to produce. Usually the same
+ * length as the data to be encrypted.
+ *
+ * @param iv
+ * The initialization vector as input to create the cipher stream.
+ * Refer to chapter 4.1.1 in RFC 3711.
+ */
+ void get_ctr_cipher_stream(uint8_t* output, uint32_t length, uint8_t* iv);
+
+ /**
+ * Counter-mode encryption.
+ *
+ * This method performs the AES CM encryption.
+ *
+ * @param input
+ * Pointer to input buffer, must be <code>inputLen</code> bytes.
+ *
+ * @param inputLen
+ * Number of bytes to process.
+ *
+ * @param output
+ * Pointer to output buffer, must be <code>inputLen</code> bytes.
+ *
+ * @param iv
+ * The initialization vector as input to create the cipher stream.
+ * Refer to chapter 4.1.1 in RFC 3711.
+ */
+ void ctr_encrypt(const uint8_t* input, uint32_t inputLen, uint8_t* output, uint8_t* iv );
+
+ /**
+ * Counter-mode encryption, in place.
+ *
+ * This method performs the AES CM encryption.
+ *
+ * @param data
+ * Pointer to input and output block, must be <code>dataLen</code>
+ * bytes.
+ *
+ * @param data_length
+ * Number of bytes to process.
+ *
+ * @param iv
+ * The initialization vector as input to create the cipher stream.
+ * Refer to chapter 4.1.1 in RFC 3711.
+ */
+ void ctr_encrypt(uint8_t* data, uint32_t data_length, uint8_t* iv );
+
+ /**
+ * Derive a AES context to compute the IV'.
+ *
+ * See chapter 4.1.2.1 in RFC 3711.
+ *
+ * @param f8Cipher
+ * Pointer to the AES context that will be used to encrypt IV to IV'
+ *
+ * @param key
+ * The master key
+ *
+ * @param keyLen
+ * Length of the master key.
+ *
+ * @param salt
+ * Master salt.
+ *
+ * @param saltLen
+ * length of master salt.
+ */
+ void f8_deriveForIV(SrtpSymCrypto* f8Cipher, uint8_t* key, int32_t keyLen, uint8_t* salt, int32_t saltLen);
+
+ /**
+ * AES F8 mode encryption, in place.
+ *
+ * This method performs the AES F8 encryption, see chapter 4.1.2
+ * in RFC 3711.
+ *
+ * @param data
+ * Pointer to input and output block, must be <code>dataLen</code>
+ * bytes.
+ *
+ * @param dataLen
+ * Number of bytes to process.
+ *
+ * @param iv
+ * The initialization vector as input to create the cipher stream.
+ * Refer to chapter 4.1.1 in RFC 3711.
+ *
+ * @param f8Cipher
+ * An AES cipher context used to encrypt IV to IV'.
+ */
+ void f8_encrypt(const uint8_t* data, uint32_t dataLen, uint8_t* iv, SrtpSymCrypto* f8Cipher);
+
+ /**
+ * AES F8 mode encryption.
+ *
+ * This method performs the AES F8 encryption, see chapter 4.1.2
+ * in RFC 3711.
+ *
+ * @param data
+ * Pointer to input and output block, must be <code>dataLen</code>
+ * bytes.
+ *
+ * @param dataLen
+ * Number of bytes to process.
+ *
+ * @param out
+ * Pointer to output buffer, must be <code>dataLen</code> bytes.
+ *
+ * @param iv
+ * The initialization vector as input to create the cipher stream.
+ * Refer to chapter 4.1.1 in RFC 3711.
+ *
+ * @param f8Cipher
+ * An AES cipher context used to encrypt IV to IV'.
+ */
+ void f8_encrypt(const uint8_t* data, uint32_t dataLen, uint8_t* out, uint8_t* iv, SrtpSymCrypto* f8Cipher);
+
+private:
+ int processBlock(F8_CIPHER_CTX* f8ctx, const uint8_t* in, int32_t length, uint8_t* out);
+ void* key;
+ int32_t algorithm;
+};
+
+#pragma GCC visibility push(default)
+int testF8();
+#pragma GCC visibility pop
+
+/* Only SrtpSymCrypto functions define the MAKE_F8_TEST */
+#ifdef MAKE_F8_TEST
+
+#include <cstring>
+#include <iostream>
+#include <cstdio>
+#include <arpa/inet.h>
+
+using namespace std;
+
+static void hexdump(const char* title, const unsigned char *s, int l)
+{
+ int n=0;
+
+ if (s == NULL) return;
+
+ fprintf(stderr, "%s",title);
+ for( ; n < l ; ++n) {
+ if((n%16) == 0)
+ fprintf(stderr, "\n%04x",n);
+ fprintf(stderr, " %02x",s[n]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * The F8 test vectors according to RFC3711
+ */
+static unsigned char salt[] = {0x32, 0xf2, 0x87, 0x0d};
+
+static unsigned char iv[] = { 0x00, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
+ 0x5c, 0x62, 0x15, 0x99, 0xd4, 0x62, 0x56, 0x4a};
+
+static unsigned char key[]= { 0x23, 0x48, 0x29, 0x00, 0x84, 0x67, 0xbe, 0x18,
+ 0x6c, 0x3d, 0xe1, 0x4a, 0xae, 0x72, 0xd6, 0x2c};
+
+static unsigned char payload[] = {
+ 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x72, 0x61,
+ 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73,
+ 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6e, 0x65, 0x78, 0x74, 0x20, 0x62, 0x65, 0x73,
+ 0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67}; // 39 bytes
+
+static unsigned char cipherText[] = {
+ 0x01, 0x9c, 0xe7, 0xa2, 0x6e, 0x78, 0x54, 0x01,
+ 0x4a, 0x63, 0x66, 0xaa, 0x95, 0xd4, 0xee, 0xfd,
+ 0x1a, 0xd4, 0x17, 0x2a, 0x14, 0xf9, 0xfa, 0xf4,
+ 0x55, 0xb7, 0xf1, 0xd4, 0xb6, 0x2b, 0xd0, 0x8f,
+ 0x56, 0x2c, 0x0e, 0xef, 0x7c, 0x48, 0x02}; // 39 bytes
+
+// static unsigned char rtpPacketHeader[] = {
+// 0x80, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
+// 0x5c, 0x62, 0x15, 0x99};
+
+static unsigned char rtpPacket[] = {
+ 0x80, 0x6e, 0x5c, 0xba, 0x50, 0x68, 0x1d, 0xe5,
+ 0x5c, 0x62, 0x15, 0x99, // header
+ 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x72, 0x61, // payload
+ 0x6e, 0x64, 0x6f, 0x6d, 0x6e, 0x65, 0x73, 0x73,
+ 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x6e, 0x65, 0x78, 0x74, 0x20, 0x62, 0x65, 0x73,
+ 0x74, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67};
+static uint32_t ROC = 0xd462564a;
+
+int testF8()
+{
+ SrtpSymCrypto* aesCipher = new SrtpSymCrypto(SrtpEncryptionAESF8);
+ SrtpSymCrypto* f8AesCipher = new SrtpSymCrypto(SrtpEncryptionAESF8);
+
+ aesCipher->setNewKey(key, sizeof(key));
+
+ /* Create the F8 IV (refer to chapter 4.1.2.2 in RFC 3711):
+ *
+ * IV = 0x00 || M || PT || SEQ || TS || SSRC || ROC
+ * 8Bit 1bit 7bit 16bit 32bit 32bit 32bit
+ * ------------\ /--------------------------------------------------
+ * XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
+ */
+
+ unsigned char derivedIv[16];
+ uint32_t* ui32p = (uint32_t*)derivedIv;
+
+ memcpy(derivedIv, rtpPacket, 12);
+ derivedIv[0] = 0;
+
+ // set ROC in network order into IV
+ ui32p[3] = htonl(ROC);
+
+ int32_t pad = 0;
+
+ if (memcmp(iv, derivedIv, 16) != 0) {
+ cerr << "Wrong IV constructed" << endl;
+ hexdump("derivedIv", derivedIv, 16);
+ hexdump("test vector Iv", iv, 16);
+ return -1;
+ }
+
+ aesCipher->f8_deriveForIV(f8AesCipher, key, sizeof(key), salt, sizeof(salt));
+
+ // now encrypt the RTP payload data
+ aesCipher->f8_encrypt(rtpPacket + 12, sizeof(rtpPacket)-12+pad,
+ derivedIv, f8AesCipher);
+
+ // compare with test vector cipher data
+ if (memcmp(rtpPacket+12, cipherText, sizeof(rtpPacket)-12+pad) != 0) {
+ cerr << "cipher data mismatch" << endl;
+ hexdump("computed cipher data", rtpPacket+12, sizeof(rtpPacket)-12+pad);
+ hexdump("Test vcetor cipher data", cipherText, sizeof(cipherText));
+ return -1;
+ }
+
+ // Now decrypt the data to get the payload data again
+ aesCipher->f8_encrypt(rtpPacket+12, sizeof(rtpPacket)-12+pad, derivedIv, f8AesCipher);
+
+ // compare decrypted data with test vector payload data
+ if (memcmp(rtpPacket+12, payload, sizeof(rtpPacket)-12+pad) != 0) {
+ cerr << "payload data mismatch" << endl;
+ hexdump("computed payload data", rtpPacket+12, sizeof(rtpPacket)-12+pad);
+ hexdump("Test vector payload data", payload, sizeof(payload));
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/**
+ * @}
+ */
+
+#endif
+
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-default-style: ellemtel
+ * c-basic-offset: 4
+ * End:
+ */
+
diff --git a/srtp/crypto/brg_endian.h b/srtp/crypto/brg_endian.h new file mode 100644 index 0000000..c03c7c5 --- /dev/null +++ b/srtp/crypto/brg_endian.h @@ -0,0 +1,148 @@ +/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 20/10/2006
+*/
+
+#ifndef BRG_ENDIAN_H
+#define BRG_ENDIAN_H
+
+#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */
+#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */
+
+/* Include files where endian defines and byteswap functions may reside */
+#if defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ )
+# include <sys/endian.h>
+#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \
+ defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ )
+# include <machine/endian.h>
+#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ )
+# if !defined( __MINGW32__ ) && !defined(AVR)
+# include <endian.h>
+# if !defined( __BEOS__ )
+# include <byteswap.h>
+# endif
+# endif
+#endif
+
+/* Now attempt to set the define for platform byte order using any */
+/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */
+/* seem to encompass most endian symbol definitions */
+
+#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN )
+# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+# endif
+#elif defined( BIG_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( LITTLE_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN )
+# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+# endif
+#elif defined( _BIG_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( _LITTLE_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN )
+# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+# endif
+#elif defined( __BIG_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( __LITTLE_ENDIAN )
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ )
+# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+# endif
+#elif defined( __BIG_ENDIAN__ )
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#elif defined( __LITTLE_ENDIAN__ )
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+
+/* if the platform byte order could not be determined, then try to */
+/* set this define using common machine defines */
+#if !defined(PLATFORM_BYTE_ORDER)
+
+#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \
+ defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \
+ defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \
+ defined( vax ) || defined( vms ) || defined( VMS ) || \
+ defined( __VMS ) || defined( _M_X64 ) || defined( AVR )
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+
+#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \
+ defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \
+ defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \
+ defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \
+ defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \
+ defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \
+ defined( THINK_C ) || defined( __VMCMS__ )
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN
+#else
+# error Please edit lines 126 or 128 in brg_endian.h to set the platform byte order
+#endif
+#endif
+
+/* special handler for IA64, which may be either endianness (?) */
+/* here we assume little-endian, but this may need to be changed */
+#if defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
+# define PLATFORM_MUST_ALIGN (1)
+#ifndef PLATFORM_BYTE_ORDER
+# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN
+#endif
+#endif
+
+#ifndef PLATFORM_MUST_ALIGN
+# define PLATFORM_MUST_ALIGN (0)
+#endif
+
+#endif /* ifndef BRG_ENDIAN_H */
diff --git a/srtp/crypto/brg_types.h b/srtp/crypto/brg_types.h new file mode 100644 index 0000000..6db737d --- /dev/null +++ b/srtp/crypto/brg_types.h @@ -0,0 +1,188 @@ +/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 1998-2006, Brian Gladman, Worcester, UK. All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue 09/09/2006
+
+ The unsigned integer types defined here are of the form uint_<nn>t where
+ <nn> is the length of the type; for example, the unsigned 32-bit type is
+ 'uint_32t'. These are NOT the same as the 'C99 integer types' that are
+ defined in the inttypes.h and stdint.h headers since attempts to use these
+ types have shown that support for them is still highly variable. However,
+ since the latter are of the form uint<nn>_t, a regular expression search
+ and replace (in VC++ search on 'uint_{:z}t' and replace with 'uint\1_t')
+ can be used to convert the types used here to the C99 standard types.
+*/
+
+#ifndef BRG_TYPES_H
+#define BRG_TYPES_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include <limits.h>
+
+#ifndef BRG_UI8
+# define BRG_UI8
+# if UCHAR_MAX == 255u
+ typedef unsigned char uint_8t;
+# else
+# error Please define uint_8t as an 8-bit unsigned integer type in brg_types.h
+# endif
+#endif
+
+#ifndef BRG_UI16
+# define BRG_UI16
+# if USHRT_MAX == 65535u
+ typedef unsigned short uint_16t;
+# else
+# error Please define uint_16t as a 16-bit unsigned short type in brg_types.h
+# endif
+#endif
+
+#ifndef BRG_UI32
+# define BRG_UI32
+# if UINT_MAX == 4294967295u
+# define li_32(h) 0x##h##u
+ typedef unsigned int uint_32t;
+# elif ULONG_MAX == 4294967295u
+# define li_32(h) 0x##h##ul
+ typedef unsigned long uint_32t;
+# elif defined( _CRAY )
+# error This code needs 32-bit data types, which Cray machines do not provide
+# else
+# error Please define uint_32t as a 32-bit unsigned integer type in brg_types.h
+# endif
+#endif
+
+#ifndef BRG_UI64
+# if defined( __BORLANDC__ ) && !defined( __MSDOS__ )
+# define BRG_UI64
+# define li_64(h) 0x##h##ui64
+ typedef unsigned __int64 uint_64t;
+# elif defined( _MSC_VER ) && ( _MSC_VER < 1300 ) /* 1300 == VC++ 7.0 */
+# define BRG_UI64
+# define li_64(h) 0x##h##ui64
+ typedef unsigned __int64 uint_64t;
+# elif defined( __sun ) && defined(ULONG_MAX) && ULONG_MAX == 0xfffffffful
+# define BRG_UI64
+# define li_64(h) 0x##h##ull
+ typedef unsigned long long uint_64t;
+# elif defined( UINT_MAX ) && UINT_MAX > 4294967295u
+# if UINT_MAX == 18446744073709551615u
+# define BRG_UI64
+# define li_64(h) 0x##h##u
+ typedef unsigned int uint_64t;
+# endif
+# elif defined( ULONG_MAX ) && ULONG_MAX > 4294967295u
+# if ULONG_MAX == 18446744073709551615ul
+# define BRG_UI64
+# define li_64(h) 0x##h##ul
+ typedef unsigned long uint_64t;
+# endif
+# elif defined( ULLONG_MAX ) && ULLONG_MAX > 4294967295u
+# if ULLONG_MAX == 18446744073709551615ull
+# define BRG_UI64
+# define li_64(h) 0x##h##ull
+ typedef unsigned long long uint_64t;
+# endif
+# elif defined( ULONG_LONG_MAX ) && ULONG_LONG_MAX > 4294967295u
+# if ULONG_LONG_MAX == 18446744073709551615ull
+# define BRG_UI64
+# define li_64(h) 0x##h##ull
+ typedef unsigned long long uint_64t;
+# endif
+# elif defined(__GNUC__) /* DLW: avoid mingw problem with -ansi */
+# define BRG_UI64
+# define li_64(h) 0x##h##ull
+ typedef unsigned long long uint_64t;
+# endif
+#endif
+
+#if defined( NEED_UINT_64T ) && !defined( BRG_UI64 )
+# error Please define uint_64t as an unsigned 64 bit type in brg_types.h
+#endif
+
+#ifndef RETURN_VALUES
+# define RETURN_VALUES
+# if defined( DLL_EXPORT )
+# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
+# define VOID_RETURN __declspec( dllexport ) void __stdcall
+# define INT_RETURN __declspec( dllexport ) int __stdcall
+# elif defined( __GNUC__ )
+# define VOID_RETURN __declspec( __dllexport__ ) void
+# define INT_RETURN __declspec( __dllexport__ ) int
+# else
+# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
+# endif
+# elif defined( DLL_IMPORT )
+# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER )
+# define VOID_RETURN __declspec( dllimport ) void __stdcall
+# define INT_RETURN __declspec( dllimport ) int __stdcall
+# elif defined( __GNUC__ )
+# define VOID_RETURN __declspec( __dllimport__ ) void
+# define INT_RETURN __declspec( __dllimport__ ) int
+# else
+# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers
+# endif
+# elif defined( __WATCOMC__ )
+# define VOID_RETURN void __cdecl
+# define INT_RETURN int __cdecl
+# else
+# define VOID_RETURN void
+# define INT_RETURN int
+# endif
+#endif
+
+/* These defines are used to declare buffers in a way that allows
+ faster operations on longer variables to be used. In all these
+ defines 'size' must be a power of 2 and >= 8
+
+ dec_unit_type(size,x) declares a variable 'x' of length
+ 'size' bits
+
+ dec_bufr_type(size,bsize,x) declares a buffer 'x' of length 'bsize'
+ bytes defined as an array of variables
+ each of 'size' bits (bsize must be a
+ multiple of size / 8)
+
+ ptr_cast(x,size) casts a pointer to a pointer to a
+ varaiable of length 'size' bits
+*/
+
+#define ui_type(size) uint_##size##t
+#define dec_unit_type(size,x) typedef ui_type(size) x
+#define dec_bufr_type(size,bsize,x) typedef ui_type(size) x[bsize / (size >> 3)]
+#define ptr_cast(x,size) ((ui_type(size)*)(x))
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/srtp/crypto/gcrypt/InitializeGcrypt.cpp b/srtp/crypto/gcrypt/InitializeGcrypt.cpp new file mode 100644 index 0000000..78fad51 --- /dev/null +++ b/srtp/crypto/gcrypt/InitializeGcrypt.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2006-2007 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> + +#include <malloc.h> +#include <pthread.h> +#include <errno.h> +#include <gcrypt.h> + +/* + * The following macro was copied from gcrypt.h and modified to explicitly + * cast the pointer types to keep the compiler happy. + */ +#define GCRY_THREAD_OPTION_PTHREAD_CPP_IMPL \ +static int gcry_pthread_mutex_init (void **priv) \ +{ \ + int err = 0; \ + pthread_mutex_t *lock = (pthread_mutex_t *)malloc (sizeof (pthread_mutex_t)); \ + \ + if (!lock) \ + err = ENOMEM; \ + if (!err) \ +{ \ + err = pthread_mutex_init (lock, NULL); \ + if (err) \ + free (lock); \ + else \ + *priv = lock; \ +} \ + return err; \ +} \ +static int gcry_pthread_mutex_destroy (void **lock) \ +{ int err = pthread_mutex_destroy ((pthread_mutex_t *)*lock); free (*lock); return err; } \ +static int gcry_pthread_mutex_lock (void **lock) \ +{ return pthread_mutex_lock ((pthread_mutex_t *)*lock); } \ +static int gcry_pthread_mutex_unlock (void **lock) \ +{ return pthread_mutex_unlock ((pthread_mutex_t *)*lock); } \ + \ +static struct gcry_thread_cbs gcry_threads_pthread = \ +{ GCRY_THREAD_OPTION_PTHREAD, NULL, \ + gcry_pthread_mutex_init, gcry_pthread_mutex_destroy, \ + gcry_pthread_mutex_lock, gcry_pthread_mutex_unlock } + +/** Implement the locking callback functions for libgcrypt. + * + */ + +static int initialized = 0; + +#ifdef __cplusplus +extern "C" { +#endif +GCRY_THREAD_OPTION_PTHREAD_CPP_IMPL; +#ifdef __cplusplus +} +#endif + +int initializeGcrypt () +{ + + if (initialized) { + return 1; + } + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + gcry_check_version(NULL); + gcry_control(GCRYCTL_DISABLE_SECMEM); + initialized = 1; + return 1; +} diff --git a/srtp/crypto/gcrypt/gcryptSrtpSymCrypto.cpp b/srtp/crypto/gcrypt/gcryptSrtpSymCrypto.cpp new file mode 100644 index 0000000..766deac --- /dev/null +++ b/srtp/crypto/gcrypt/gcryptSrtpSymCrypto.cpp @@ -0,0 +1,344 @@ +/* + Copyright (C) 2005, 2004, 2012 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. +*/ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +extern void initializeGcrypt(); + +#define MAKE_F8_TEST + +#include <gcrypt.h> // the include of gcrypt +#include <stdlib.h> +#include <crypto/SrtpSymCrypto.h> +#include <crypto/twofish.h> + +#include <stdio.h> + +SrtpSymCrypto::SrtpSymCrypto(int algo) : key(NULL), algorithm(algo) { + initializeGcrypt(); +} + +SrtpSymCrypto::SrtpSymCrypto( uint8_t* k, int32_t keyLength, int algo) : + key(NULL), algorithm(algo) { + + initializeGcrypt(); + setNewKey(k, keyLength); +} + +SrtpSymCrypto::~SrtpSymCrypto() { + if (key) { + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) + gcry_cipher_close(static_cast<gcry_cipher_hd_t>(key)); + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) { + memset(key, 0, sizeof(Twofish_key)); + delete[] (uint8_t*)key; + } + key = NULL; + } +} + +static int twoFishInit = 0; + +bool SrtpSymCrypto::setNewKey(const uint8_t* k, int32_t keyLength) { + + // release an existing key before setting a new one + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) { + if (key != NULL) { + gcry_cipher_close(static_cast<gcry_cipher_hd_t>(key)); + key = NULL; + } + + int algo = 0; + if (keyLength == 16) { + algo = GCRY_CIPHER_AES; + } + else if (keyLength == 32) { + algo = GCRY_CIPHER_AES256; + } + else { + return false; + } + gcry_cipher_hd_t tmp; + gcry_cipher_open(&tmp, algo, GCRY_CIPHER_MODE_ECB, 0); + key = tmp; + gcry_cipher_setkey(static_cast<gcry_cipher_hd_t>(key), k, keyLength); + } + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) { + if (!twoFishInit) { + Twofish_initialise(); + twoFishInit = 1; + } + if (key != NULL) + delete[] (uint8_t*)key; + + key = new uint8_t[sizeof(Twofish_key)]; + memset(key, 0, sizeof(Twofish_key)); + Twofish_prepare_key((Twofish_Byte*)k, keyLength, (Twofish_key*)key); + } + else + return false; + + return true; +} + + +void SrtpSymCrypto::encrypt(const uint8_t* input, uint8_t* output) { + if (key != NULL) { + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) + gcry_cipher_encrypt (static_cast<gcry_cipher_hd_t>(key), + output, SRTP_BLOCK_SIZE, input, SRTP_BLOCK_SIZE); + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) + Twofish_encrypt((Twofish_key*)key, (Twofish_Byte*)input, + (Twofish_Byte*)output); + } +} + +void SrtpSymCrypto::get_ctr_cipher_stream( uint8_t* output, uint32_t length, + uint8_t* iv ) { + uint16_t ctr = 0; + + unsigned char temp[SRTP_BLOCK_SIZE]; + + for(ctr = 0; ctr < length/SRTP_BLOCK_SIZE; ctr++ ){ + //compute the cipher stream + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, &output[ctr*SRTP_BLOCK_SIZE]); + } + if ((length % SRTP_BLOCK_SIZE) > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + memcpy(&output[ctr*SRTP_BLOCK_SIZE], temp, length % SRTP_BLOCK_SIZE); + } +} + +void SrtpSymCrypto::ctr_encrypt( const uint8_t* input, uint32_t input_length, + uint8_t* output, uint8_t* iv ) { + + if (key == NULL) + return; + + uint16_t ctr = 0; + unsigned char temp[SRTP_BLOCK_SIZE]; + + int l = input_length/SRTP_BLOCK_SIZE; + for ( ctr = 0; ctr < l; ctr++ ) { + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) { + *output++ = temp[i] ^ *input++; + } + + } + l = input_length % SRTP_BLOCK_SIZE; + if (l > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < l; i++ ) { + *output++ = temp[i] ^ *input++; + } + } +} + +void SrtpSymCrypto::ctr_encrypt( uint8_t* data, uint32_t data_length, uint8_t* iv ) { + + if (key == NULL) + return; + + uint16_t ctr = 0; + unsigned char temp[SRTP_BLOCK_SIZE]; + + int l = data_length/SRTP_BLOCK_SIZE; + for (ctr = 0; ctr < l; ctr++ ) { + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) { + *data++ ^= temp[i]; + } + + } + l = data_length % SRTP_BLOCK_SIZE; + if (l > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < l; i++ ) { + *data++ ^= temp[i]; + } + } + +} + +void SrtpSymCrypto::f8_encrypt(const uint8_t* data, uint32_t data_length, uint8_t* iv, SrtpSymCrypto* f8Cipher ) { + + f8_encrypt(data, data_length, const_cast<uint8_t*>(data), iv, f8Cipher); +} + +#define MAX_KEYLEN 32 + +void SrtpSymCrypto::f8_deriveForIV(SrtpSymCrypto* f8Cipher, uint8_t* key, int32_t keyLen, + uint8_t* salt, int32_t saltLen) { + + unsigned char *cp_in, *cp_in1, *cp_out; + + unsigned char maskedKey[MAX_KEYLEN]; + unsigned char saltMask[MAX_KEYLEN]; + + if (keyLen > MAX_KEYLEN) + return; + + if (saltLen > keyLen) + return; + /* + * First copy the salt into the mask field, then fill with 0x55 to + * get a full key. + */ + memcpy(saltMask, salt, saltLen); + memset(saltMask+saltLen, 0x55, keyLen-saltLen); + + /* + * XOR the original key with the above created mask to + * get the special key. + */ + cp_out = maskedKey; + cp_in = key; + cp_in1 = saltMask; + for (int i = 0; i < keyLen; i++) { + *cp_out++ = *cp_in++ ^ *cp_in1++; + } + /* + * Prepare the a new AES cipher with the special key to compute IV' + */ + f8Cipher->setNewKey(maskedKey, keyLen); +} + +void SrtpSymCrypto::f8_encrypt(const uint8_t* in, uint32_t in_length, uint8_t* out, + uint8_t* iv, SrtpSymCrypto* f8Cipher ) { + + int offset = 0; + + unsigned char ivAccent[SRTP_BLOCK_SIZE]; + unsigned char S[SRTP_BLOCK_SIZE]; + + F8_CIPHER_CTX f8ctx; + + if (key == NULL) + return; + + /* + * Get memory for the derived IV (IV') + */ + f8ctx.ivAccent = ivAccent; + /* + * Use the derived IV encryption setup to encrypt the original IV to produce IV'. + */ + f8Cipher->encrypt(iv, f8ctx.ivAccent); + + f8ctx.J = 0; // initialize the counter + f8ctx.S = S; // get the key stream buffer + + memset(f8ctx.S, 0, SRTP_BLOCK_SIZE); // initial value for key stream + + while (in_length >= SRTP_BLOCK_SIZE) { + processBlock(&f8ctx, in+offset, SRTP_BLOCK_SIZE, out+offset); + in_length -= SRTP_BLOCK_SIZE; + offset += SRTP_BLOCK_SIZE; + } + if (in_length > 0) { + processBlock(&f8ctx, in+offset, in_length, out+offset); + } +} + +int SrtpSymCrypto::processBlock(F8_CIPHER_CTX *f8ctx, const uint8_t* in, int32_t length, uint8_t* out) { + + int i; + const uint8_t *cp_in; + uint8_t* cp_in1, *cp_out; + uint32_t *ui32p; + + /* + * XOR the previous key stream with IV' + * ( S(-1) xor IV' ) + */ + cp_in = f8ctx->ivAccent; + cp_out = f8ctx->S; + for (i = 0; i < SRTP_BLOCK_SIZE; i++) { + *cp_out++ ^= *cp_in++; + } + /* + * Now XOR (S(n-1) xor IV') with the current counter, then increment the counter + */ + ui32p = (uint32_t *)f8ctx->S; + ui32p[3] ^= htonl(f8ctx->J); + f8ctx->J++; + /* + * Now compute the new key stream using encrypt + */ + encrypt(f8ctx->S, f8ctx->S); + /* + * as the last step XOR the plain text with the key stream to produce + * the ciphertext. + */ + cp_out = out; + cp_in = in; + cp_in1 = f8ctx->S; + for (i = 0; i < length; i++) { + *cp_out++ = *cp_in++ ^ *cp_in1++; + } + return length; +} + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + + diff --git a/srtp/crypto/gcrypt/gcrypthmac.cpp b/srtp/crypto/gcrypt/gcrypthmac.cpp new file mode 100644 index 0000000..208c3ec --- /dev/null +++ b/srtp/crypto/gcrypt/gcrypthmac.cpp @@ -0,0 +1,117 @@ +/* + Copyright (C) 2005, 2004 Erik Eliasson, Johan Bilien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + */ +#include <gcrypt.h> + +#include <crypto/hmac.h> +#include <stdio.h> + +void hmac_sha1(uint8_t* key, int32_t keyLength, + const uint8_t* data, int32_t dataLength, + uint8_t* mac, int32_t* macLength) +{ + gcry_md_hd_t hd; + + gcry_md_open(&hd, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + gcry_md_write (hd, data, dataLength); + + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA1); + memcpy(mac, p, SHA1_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA1_DIGEST_LENGTH; + } + gcry_md_close (hd); +} + +void hmac_sha1( uint8_t* key, int32_t keyLength, + const uint8_t* dataChunks[], + uint32_t dataChunkLength[], + uint8_t* mac, int32_t* macLength ) +{ + gcry_md_hd_t hd; + + gcry_md_open(&hd, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(hd, key, keyLength); + + while (*dataChunks) { + gcry_md_write (hd, *dataChunks, (uint32_t)(*dataChunkLength)); + dataChunks++; + dataChunkLength++; + } + uint8_t* p = gcry_md_read (hd, GCRY_MD_SHA1); + memcpy(mac, p, SHA1_DIGEST_LENGTH); + if (macLength != NULL) { + *macLength = SHA1_DIGEST_LENGTH; + } + gcry_md_close (hd); +} + +void* createSha1HmacContext(uint8_t* key, int32_t key_length) +{ + gcry_md_hd_t ctx; + + gcry_md_open(&ctx, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + gcry_md_setkey(ctx, key, key_length); + return ctx; +} + +void hmacSha1Ctx(void* ctx, const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t* mac_length) +{ + gcry_md_hd_t pctx = (gcry_md_hd_t)ctx; + + gcry_md_reset(pctx); + + gcry_md_write (pctx, data, data_length); + + uint8_t* p = gcry_md_read (pctx, GCRY_MD_SHA1); + memcpy(mac, p, SHA1_DIGEST_LENGTH); + if (mac_length != NULL) { + *mac_length = SHA1_DIGEST_LENGTH; + } +} + +void hmacSha1Ctx(void* ctx, const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t* mac_length ) +{ + gcry_md_hd_t pctx = (gcry_md_hd_t)ctx; + + gcry_md_reset (pctx); + while (*data) { + gcry_md_write (pctx, *data, (uint32_t)(*data_length)); + data++; + data_length++; + } + uint8_t* p = gcry_md_read (pctx, GCRY_MD_SHA1); + memcpy(mac, p, SHA1_DIGEST_LENGTH); + if (mac_length != NULL) { + *mac_length = SHA1_DIGEST_LENGTH; + } +} + +void freeSha1HmacContext(void* ctx) +{ + gcry_md_hd_t pctx = (gcry_md_hd_t)ctx; + gcry_md_close (pctx); +} diff --git a/srtp/crypto/hmac.h b/srtp/crypto/hmac.h new file mode 100644 index 0000000..4abfa8f --- /dev/null +++ b/srtp/crypto/hmac.h @@ -0,0 +1,172 @@ +/* + Copyright (C) 2005, 2004, 2010 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** + * Functions to compute SHA1 HAMAC. + * + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Werner Dittmann + */ + +#ifndef HMAC_H +#define HMAC_H + +/** + * @file hmac.h + * @brief Functions that provide SHA1 HMAC support + * + * @ingroup GNU_ZRTP + * @{ + */ + +#include <stdint.h> + +#ifndef SHA1_DIGEST_LENGTH +#define SHA1_DIGEST_LENGTH 20 +#endif + +/** + * Compute SHA1 HMAC. + * + * This functions takes one data chunk and computes its SHA1 HMAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 20 bytes (SHA1_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ + +void hmac_sha1( uint8_t* key, int32_t key_length, + const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t* mac_length ); + +/** + * Compute SHA1 HMAC over several data cunks. + * + * This functions takes several data chunk and computes the SHA1 HAMAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 20 bytes (SHA1_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ +void hmac_sha1( uint8_t* key, int32_t key_length, + const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t* mac_length ); + +/** + * Create and initialize a SHA1 HMAC context. + * + * An application uses this context to create several HMAC with the same key. + * + * @param key + * The MAC key. + * @param key_length + * Lenght of the MAC key in bytes + * @return Returns a pointer to the initialized context + */ +void* createSha1HmacContext(uint8_t* key, int32_t key_length); + +/** + * Compute SHA1 HMAC. + * + * This functions takes one data chunk and computes its SHA1 HMAC. On return + * the SHA1 MAC context is ready to compute a HMAC for another data chunk. + * + * @param ctx + * Pointer to initialized SHA1 HMAC context + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 20 bytes (SHA1_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ +void hmacSha1Ctx(void* ctx, const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t* mac_length ); + +/** + * Compute SHA1 HMAC over several data cunks. + * + * This functions takes several data chunks and computes the SHA1 HAMAC. On return + * the SHA1 MAC context is ready to compute a HMAC for another data chunk. + * + * @param ctx + * Pointer to initialized SHA1 HMAC context + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. This + * buffer must have a size of at least 20 bytes (SHA1_DIGEST_LENGTH). + * @param mac_length + * Point to an integer that receives the length of the computed HMAC. + */ +void hmacSha1Ctx(void* ctx, const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t* mac_length ); + +/** + * Free SHA1 HMAC context. + * + * @param ctx a pointer to SHA1 HMAC context + */ +void freeSha1HmacContext(void* ctx); + + +/** + * @} + */ +#endif diff --git a/srtp/crypto/macSkein.cpp b/srtp/crypto/macSkein.cpp new file mode 100644 index 0000000..ba4c260 --- /dev/null +++ b/srtp/crypto/macSkein.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <crypto/macSkein.h> +#include <stdlib.h> + +void macSkein(uint8_t* key, int32_t key_length, + const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t mac_length, SkeinSize_t skeinSize) +{ + SkeinCtx_t ctx; + + skeinCtxPrepare(&ctx, skeinSize); + + skeinMacInit(&ctx, key, key_length, mac_length); + skeinUpdate(&ctx, data, data_length); + skeinFinal(&ctx, mac); +} + +void macSkein(uint8_t* key, int32_t key_length, + const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t mac_length, SkeinSize_t skeinSize) +{ + SkeinCtx_t ctx; + + skeinCtxPrepare(&ctx, skeinSize); + + skeinMacInit(&ctx, key, key_length, mac_length); + while (*data) { + skeinUpdate(&ctx, *data, *data_length); + data++; + data_length ++; + } + skeinFinal(&ctx, mac); +} + +void* createSkeinMacContext(uint8_t* key, int32_t key_length, + int32_t mac_length, SkeinSize_t skeinSize) +{ + SkeinCtx_t* ctx = (SkeinCtx_t*)malloc(sizeof(SkeinCtx_t)); + + skeinCtxPrepare(ctx, skeinSize); + skeinMacInit(ctx, key, key_length, mac_length); + return ctx; +} + +void macSkeinCtx(void* ctx, const uint8_t* data, uint32_t data_length, + uint8_t* mac) +{ + SkeinCtx_t* pctx = (SkeinCtx_t*)ctx; + + skeinUpdate(pctx, data, data_length); + skeinFinal(pctx, mac); + skeinReset(pctx); +} + +void macSkeinCtx(void* ctx, const uint8_t* data[], uint32_t data_length[], + uint8_t* mac) +{ + SkeinCtx_t* pctx = (SkeinCtx_t*)ctx; + + while (*data) { + skeinUpdate(pctx, *data, *data_length); + data++; + data_length++; + } + skeinFinal(pctx, mac); + skeinReset(pctx); +} + +void freeSkeinMacContext(void* ctx) +{ + if (ctx) + free(ctx); +} diff --git a/srtp/crypto/macSkein.h b/srtp/crypto/macSkein.h new file mode 100644 index 0000000..71c2ad9 --- /dev/null +++ b/srtp/crypto/macSkein.h @@ -0,0 +1,148 @@ +/* + Copyright (C) 2010 Werner Dittmann + + 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 <http://www.gnu.org/licenses/>. +*/ + + +#ifndef MAC_SKEIN_H +#define MAC_SKEIN_H + +#include <crypto/skeinApi.h> +/** + * @file macSkein.h + * @brief Function that provide Skein MAC support + * + * + * Functions to compute Skein MAC. + * + * @ingroup GNU_ZRTP + * @{ + */ + +/** + * Compute Skein MAC. + * + * This functions takes one data chunk and computes its Skein MAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. + * @param mac_length + * Integer that contains the length of the MAC in bits (not bytes). + * @param skeinSize + * The Skein size to use. + */ +void macSkein( uint8_t* key, int32_t key_length, + const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t mac_length, SkeinSize_t skeinSize ); + +/** + * Compute Skein MAC over several data cunks. + * + * This functions takes several data chunk and computes the Skein MAC. + * + * @param key + * The MAC key. + * @param key_length + * Lneght of the MAC key in bytes + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. + * @param mac_length + * Integer that contains the length of the MAC in bits (not bytes). + * @param skeinSize + * The Skein size to use. + */ +void macSkein( uint8_t* key, int32_t key_length, + const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t mac_length, SkeinSize_t skeinSize); + +/** + * Create and initialize a Skein MAC context. + * + * An application uses this context to hash several data with on Skein MAC + * Context with the same key, key length and mac length + * + * @param key + * The MAC key. + * @param key_length + * Lenght of the MAC key in bytes + * @param mac_length + * Integer that contains the length of the MAC in bits (not bytes). + * @param skeinSize + * The Skein size to use. + * @return Returns a pointer to the initialized context + */ +void* createSkeinMacContext(uint8_t* key, int32_t key_length, + int32_t mac_length, SkeinSize_t skeinSize); + +/** + * Compute Skein MAC. + * + * This functions takes one data chunk and computes its Skein MAC. + * + * @param ctx + * Pointer to initialized Skein MAC context + * @param data + * Points to the data chunk. + * @param data_length + * Length of the data in bytes + * @param mac + * Points to a buffer that receives the computed digest. + */ + +void macSkeinCtx(void* ctx, const uint8_t* data, uint32_t data_length, + uint8_t* mac); + +/** + * Compute Skein MAC over several data cunks. + * + * This functions takes several data chunk and computes the SHA1 HAMAC. + * + * @param ctx + * Pointer to initialized Skein MAC context + * @param data + * Points to an array of pointers that point to the data chunks. A NULL + * pointer in an array element terminates the data chunks. + * @param data_length + * Points to an array of integers that hold the length of each data chunk. + * @param mac + * Points to a buffer that receives the computed digest. + */ +void macSkeinCtx(void* ctx, const uint8_t* data[], uint32_t data_length[], + uint8_t* mac); + +/** + * Free Skein MAC context. + * + * @param ctx a pointer to Skein MAC context + */ +void freeSkeinMacContext(void* ctx); + +/** + * @} + */ +#endif
\ No newline at end of file diff --git a/srtp/crypto/openssl/.kdev_include_paths b/srtp/crypto/openssl/.kdev_include_paths new file mode 100644 index 0000000..e7d94bb --- /dev/null +++ b/srtp/crypto/openssl/.kdev_include_paths @@ -0,0 +1 @@ +/home/werner/devhome/pjproject.git/third_party/zsrtp/include/ diff --git a/srtp/crypto/openssl/SrtpSymCrypto.cpp b/srtp/crypto/openssl/SrtpSymCrypto.cpp new file mode 100644 index 0000000..3d6747d --- /dev/null +++ b/srtp/crypto/openssl/SrtpSymCrypto.cpp @@ -0,0 +1,326 @@ +/* + Copyright (C) 2005, 2004, 2012 Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/** + * @author Erik Eliasson <eliasson@it.kth.se> + * @author Johan Bilien <jobi@via.ecp.fr> + * @author Werner Dittmann <Werner.Dittmann@t-online.de> + */ + +#define MAKE_F8_TEST + +#include <stdlib.h> +#include <openssl/aes.h> // the include of openSSL +#include <crypto/SrtpSymCrypto.h> +#include <crypto/twofish.h> +#include <string.h> +#include <stdio.h> +#include <arpa/inet.h> + +SrtpSymCrypto::SrtpSymCrypto(int algo):key(NULL), algorithm(algo) { +} + +SrtpSymCrypto::SrtpSymCrypto( uint8_t* k, int32_t keyLength, int algo ): + key(NULL), algorithm(algo) { + + setNewKey(k, keyLength); +} + +SrtpSymCrypto::~SrtpSymCrypto() { + if (key != NULL) { + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) { + memset(key, 0, sizeof(AES_KEY) ); + } + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) { + memset(key, 0, sizeof(Twofish_key)); + } + delete[] (uint8_t*)key; + key = NULL; + } +} + +static int twoFishInit = 0; + +bool SrtpSymCrypto::setNewKey(const uint8_t* k, int32_t keyLength) { + // release an existing key before setting a new one + if (key != NULL) + delete[] (uint8_t*)key; + + if (!(keyLength == 16 || keyLength == 32)) { + return false; + } + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) { + key = new uint8_t[sizeof(AES_KEY)]; + memset(key, 0, sizeof(AES_KEY) ); + AES_set_encrypt_key(k, keyLength*8, (AES_KEY *)key); + } + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) { + if (!twoFishInit) { + Twofish_initialise(); + twoFishInit = 1; + } + key = new uint8_t[sizeof(Twofish_key)]; + memset(key, 0, sizeof(Twofish_key)); + Twofish_prepare_key((Twofish_Byte*)k, keyLength, (Twofish_key*)key); + } + else + return false; + + return true; +} + + +void SrtpSymCrypto::encrypt(const uint8_t* input, uint8_t* output ) { + if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) { + AES_encrypt(input, output, (AES_KEY *)key); + } + else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) { + Twofish_encrypt((Twofish_key*)key, (Twofish_Byte*)input, + (Twofish_Byte*)output); + } +} + +void SrtpSymCrypto::get_ctr_cipher_stream(uint8_t* output, uint32_t length, + uint8_t* iv ) { + uint16_t ctr = 0; + unsigned char temp[SRTP_BLOCK_SIZE]; + + for(ctr = 0; ctr < length/SRTP_BLOCK_SIZE; ctr++) { + //compute the cipher stream + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, &output[ctr*SRTP_BLOCK_SIZE]); + } + if ((length % SRTP_BLOCK_SIZE) > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + memcpy(&output[ctr*SRTP_BLOCK_SIZE], temp, length % SRTP_BLOCK_SIZE ); + } +} + +void SrtpSymCrypto::ctr_encrypt(const uint8_t* input, uint32_t input_length, + uint8_t* output, uint8_t* iv ) { + + if (key == NULL) + return; + + uint16_t ctr = 0; + unsigned char temp[SRTP_BLOCK_SIZE]; + + int l = input_length/SRTP_BLOCK_SIZE; + for (ctr = 0; ctr < l; ctr++ ) { + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) { + *output++ = temp[i] ^ *input++; + } + + } + l = input_length % SRTP_BLOCK_SIZE; + if (l > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < l; i++ ) { + *output++ = temp[i] ^ *input++; + } + } +} + +void SrtpSymCrypto::ctr_encrypt( uint8_t* data, uint32_t data_length, uint8_t* iv ) { + + if (key == NULL) + return; + + uint16_t ctr = 0; + unsigned char temp[SRTP_BLOCK_SIZE]; + + int l = data_length/SRTP_BLOCK_SIZE; + for (ctr = 0; ctr < l; ctr++ ) { + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) { + *data++ ^= temp[i]; + } + + } + l = data_length % SRTP_BLOCK_SIZE; + if (l > 0) { + // Treat the last bytes: + iv[14] = (uint8_t)((ctr & 0xFF00) >> 8); + iv[15] = (uint8_t)((ctr & 0x00FF)); + + encrypt(iv, temp); + for (int i = 0; i < l; i++ ) { + *data++ ^= temp[i]; + } + } +} + +void SrtpSymCrypto::f8_encrypt(const uint8_t* data, uint32_t data_length, + uint8_t* iv, SrtpSymCrypto* f8Cipher ) { + + f8_encrypt(data, data_length, const_cast<uint8_t*>(data), iv, f8Cipher); +} + +#define MAX_KEYLEN 32 + +void SrtpSymCrypto::f8_deriveForIV(SrtpSymCrypto* f8Cipher, uint8_t* key, int32_t keyLen, + uint8_t* salt, int32_t saltLen) { + + unsigned char *cp_in, *cp_in1, *cp_out; + + unsigned char maskedKey[MAX_KEYLEN]; + unsigned char saltMask[MAX_KEYLEN]; + + if (keyLen > MAX_KEYLEN) + return; + + if (saltLen > keyLen) + return; + /* + * First copy the salt into the mask field, then fill with 0x55 to + * get a full key. + */ + memcpy(saltMask, salt, saltLen); + memset(saltMask+saltLen, 0x55, keyLen-saltLen); + + /* + * XOR the original key with the above created mask to + * get the special key. + */ + cp_out = maskedKey; + cp_in = key; + cp_in1 = saltMask; + for (int i = 0; i < keyLen; i++) { + *cp_out++ = *cp_in++ ^ *cp_in1++; + } + /* + * Prepare the a new AES cipher with the special key to compute IV' + */ + f8Cipher->setNewKey(maskedKey, keyLen); +} + +void SrtpSymCrypto::f8_encrypt(const uint8_t* in, uint32_t in_length, uint8_t* out, + uint8_t* iv, SrtpSymCrypto* f8Cipher ) { + + + int offset = 0; + + unsigned char ivAccent[SRTP_BLOCK_SIZE]; + unsigned char S[SRTP_BLOCK_SIZE]; + + F8_CIPHER_CTX f8ctx; + + if (key == NULL) + return; + /* + * Get memory for the derived IV (IV') + */ + f8ctx.ivAccent = ivAccent; + /* + * Use the derived IV encryption setup to encrypt the original IV to produce IV'. + */ + f8Cipher->encrypt(iv, f8ctx.ivAccent); + + f8ctx.J = 0; // initialize the counter + f8ctx.S = S; // get the key stream buffer + + memset(f8ctx.S, 0, SRTP_BLOCK_SIZE); // initial value for key stream + + while (in_length >= SRTP_BLOCK_SIZE) { + processBlock(&f8ctx, in+offset, SRTP_BLOCK_SIZE, out+offset); + in_length -= SRTP_BLOCK_SIZE; + offset += SRTP_BLOCK_SIZE; + } + if (in_length > 0) { + processBlock(&f8ctx, in+offset, in_length, out+offset); + } +} + +int SrtpSymCrypto::processBlock(F8_CIPHER_CTX *f8ctx, const uint8_t* in, int32_t length, uint8_t* out) { + + int i; + const uint8_t *cp_in; + uint8_t* cp_in1, *cp_out; + uint32_t *ui32p; + + /* + * XOR the previous key stream with IV' + * ( S(-1) xor IV' ) + */ + cp_in = f8ctx->ivAccent; + cp_out = f8ctx->S; + for (i = 0; i < SRTP_BLOCK_SIZE; i++) { + *cp_out++ ^= *cp_in++; + } + /* + * Now XOR (S(n-1) xor IV') with the current counter, then increment the counter + */ + ui32p = (uint32_t *)f8ctx->S; + ui32p[3] ^= htonl(f8ctx->J); + f8ctx->J++; + /* + * Now compute the new key stream using AES encrypt + */ + encrypt(f8ctx->S, f8ctx->S); + /* + * as the last step XOR the plain text with the key stream to produce + * the ciphertext. + */ + cp_out = out; + cp_in = in; + cp_in1 = f8ctx->S; + for (i = 0; i < length; i++) { + *cp_out++ = *cp_in++ ^ *cp_in1++; + } + return length; +} + + +/** EMACS ** + * Local variables: + * mode: c++ + * c-default-style: ellemtel + * c-basic-offset: 4 + * End: + */ + diff --git a/srtp/crypto/openssl/hmac.cpp b/srtp/crypto/openssl/hmac.cpp new file mode 100644 index 0000000..88d33a1 --- /dev/null +++ b/srtp/crypto/openssl/hmac.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2005, 2004, 2010, Erik Eliasson, Johan Bilien, Werner Dittmann + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + +/* + * Authors: Erik Eliasson <eliasson@it.kth.se> + * Johan Bilien <jobi@via.ecp.fr> + * Werner Dittmann + */ + +#include <stdint.h> +#include <openssl/hmac.h> +#include <crypto/hmac.h> + +void hmac_sha1(uint8_t * key, int32_t key_length, + const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t* mac_length ) +{ + HMAC(EVP_sha1(), key, key_length, + data, data_length, mac, + reinterpret_cast<uint32_t*>(mac_length) ); +} + +void hmac_sha1( uint8_t* key, int32_t key_length, + const uint8_t* data_chunks[], + uint32_t data_chunck_length[], + uint8_t* mac, int32_t* mac_length ) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, key, key_length, EVP_sha1(), NULL); + while (*data_chunks) { + HMAC_Update(&ctx, *data_chunks, *data_chunck_length); + data_chunks ++; + data_chunck_length ++; + } + HMAC_Final(&ctx, mac, reinterpret_cast<uint32_t*>(mac_length)); + HMAC_CTX_cleanup(&ctx); +} + +void* createSha1HmacContext(uint8_t* key, int32_t key_length) +{ + HMAC_CTX* ctx = (HMAC_CTX*)malloc(sizeof(HMAC_CTX)); + + HMAC_CTX_init(ctx); + HMAC_Init_ex(ctx, key, key_length, EVP_sha1(), NULL); + return ctx; +} + +void hmacSha1Ctx(void* ctx, const uint8_t* data, uint32_t data_length, + uint8_t* mac, int32_t* mac_length) +{ + HMAC_CTX* pctx = (HMAC_CTX*)ctx; + + HMAC_Init_ex(pctx, NULL, 0, NULL, NULL ); + HMAC_Update(pctx, data, data_length ); + HMAC_Final(pctx, mac, reinterpret_cast<uint32_t*>(mac_length) ); +} + +void hmacSha1Ctx(void* ctx, const uint8_t* data[], uint32_t data_length[], + uint8_t* mac, int32_t* mac_length ) +{ + HMAC_CTX* pctx = (HMAC_CTX*)ctx; + + HMAC_Init_ex(pctx, NULL, 0, NULL, NULL ); + while (*data) { + HMAC_Update(pctx, *data, *data_length); + data++; + data_length++; + } + HMAC_Final(pctx, mac, reinterpret_cast<uint32_t*>(mac_length) ); +} + +void freeSha1HmacContext(void* ctx) +{ + if (ctx) { + HMAC_CTX_cleanup((HMAC_CTX*)ctx); + free(ctx); + } +}
\ No newline at end of file diff --git a/srtp/crypto/skein.c b/srtp/crypto/skein.c new file mode 100644 index 0000000..5935a2a --- /dev/null +++ b/srtp/crypto/skein.c @@ -0,0 +1,742 @@ +/***********************************************************************
+**
+** Implementation of the Skein hash function.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+************************************************************************/
+
+#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */
+
+#include <string.h> /* get the memcpy/memset functions */
+#include <crypto/skein.h> /* get the Skein API definitions */
+#include <crypto/skein_iv.h> /* get precomputed IVs */
+
+/*****************************************************************/
+/* External function to process blkCnt (nonzero) full block(s) of data. */
+void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd);
+void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd);
+void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd);
+
+/*****************************************************************/
+/* 256-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen)
+{
+ union
+ {
+ u08b_t b[SKEIN_256_STATE_BYTES];
+ u64b_t w[SKEIN_256_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+
+ switch (hashBitLen)
+ { /* use pre-computed values, where available */
+ case 256:
+ memcpy(ctx->X,SKEIN_256_IV_256,sizeof(ctx->X));
+ break;
+ case 224:
+ memcpy(ctx->X,SKEIN_256_IV_224,sizeof(ctx->X));
+ break;
+ case 160:
+ memcpy(ctx->X,SKEIN_256_IV_160,sizeof(ctx->X));
+ break;
+ case 128:
+ memcpy(ctx->X,SKEIN_256_IV_128,sizeof(ctx->X));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /* build/process the config block, type == CONFIG (could be precomputed) */
+ Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */
+
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */
+
+ /* compute the initial chaining values from config block */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */
+ Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+ break;
+ }
+ /* The chaining vars ctx->X are now initialized for the given hashBitLen. */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to Skein_256_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int Skein_256_InitExt(Skein_256_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes)
+{
+ union
+ {
+ u08b_t b[SKEIN_256_STATE_BYTES];
+ u64b_t w[SKEIN_256_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->X[], based on key */
+ if (keyBytes == 0) /* is there a key? */
+ {
+ memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */
+ }
+ else /* here to pre-process a key */
+ {
+ Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X));
+ /* do a mini-Init right here */
+ ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */
+ Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */
+ Skein_256_Update(ctx,key,keyBytes); /* hash the key */
+ Skein_256_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */
+ memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */
+#if SKEIN_NEED_SWAP
+ {
+ uint_t i;
+ for (i=0;i<SKEIN_256_STATE_WORDS;i++) /* convert key bytes to context words */
+ ctx->X[i] = Skein_Swap64(ctx->X[i]);
+ }
+#endif
+ }
+ /* build/process the config block, type == CONFIG (could be precomputed for each key) */
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+ Skein_Start_New_Type(ctx,CFG_FINAL);
+
+ memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+
+ Skein_Show_Key(256,&ctx->h,key,keyBytes);
+
+ /* compute the initial chaining values from config block */
+ Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->X are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt)
+{
+ size_t n;
+
+ Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* process full blocks, if any */
+ if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES)
+ {
+ if (ctx->h.bCnt) /* finish up any buffered message data */
+ {
+ n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */
+ if (n)
+ {
+ Skein_assert(n < msgByteCnt); /* check on our logic here */
+ memcpy(&ctx->b[ctx->h.bCnt],msg,n);
+ msgByteCnt -= n;
+ msg += n;
+ ctx->h.bCnt += n;
+ }
+ Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES);
+ Skein_256_Process_Block(ctx,ctx->b,1,SKEIN_256_BLOCK_BYTES);
+ ctx->h.bCnt = 0;
+ }
+ /* now process any remaining full blocks, directly from input message data */
+ if (msgByteCnt > SKEIN_256_BLOCK_BYTES)
+ {
+ n = (msgByteCnt-1) / SKEIN_256_BLOCK_BYTES; /* number of full blocks to process */
+ Skein_256_Process_Block(ctx,msg,n,SKEIN_256_BLOCK_BYTES);
+ msgByteCnt -= n * SKEIN_256_BLOCK_BYTES;
+ msg += n * SKEIN_256_BLOCK_BYTES;
+ }
+ Skein_assert(ctx->h.bCnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msgByteCnt)
+ {
+ Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt);
+ ctx->h.bCnt += msgByteCnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int Skein_256_Final(Skein_256_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN_256_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt);
+
+ Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN_256_BLOCK_BYTES)
+ n = SKEIN_256_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*****************************************************************/
+/* 512-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen)
+{
+ union
+ {
+ u08b_t b[SKEIN_512_STATE_BYTES];
+ u64b_t w[SKEIN_512_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+
+ switch (hashBitLen)
+ { /* use pre-computed values, where available */
+ case 512:
+ memcpy(ctx->X,SKEIN_512_IV_512,sizeof(ctx->X));
+ break;
+ case 384:
+ memcpy(ctx->X,SKEIN_512_IV_384,sizeof(ctx->X));
+ break;
+ case 256:
+ memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X));
+ break;
+ case 224:
+ memcpy(ctx->X,SKEIN_512_IV_224,sizeof(ctx->X));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /* build/process the config block, type == CONFIG (could be precomputed) */
+ Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */
+
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */
+
+ /* compute the initial chaining values from config block */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */
+ Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+ break;
+ }
+
+ /* The chaining vars ctx->X are now initialized for the given hashBitLen. */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to Skein_512_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int Skein_512_InitExt(Skein_512_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes)
+{
+ union
+ {
+ u08b_t b[SKEIN_512_STATE_BYTES];
+ u64b_t w[SKEIN_512_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->X[], based on key */
+ if (keyBytes == 0) /* is there a key? */
+ {
+ memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */
+ }
+ else /* here to pre-process a key */
+ {
+ Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X));
+ /* do a mini-Init right here */
+ ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */
+ Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */
+ Skein_512_Update(ctx,key,keyBytes); /* hash the key */
+ Skein_512_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */
+ memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */
+#if SKEIN_NEED_SWAP
+ {
+ uint_t i;
+ for (i=0;i<SKEIN_512_STATE_WORDS;i++) /* convert key bytes to context words */
+ ctx->X[i] = Skein_Swap64(ctx->X[i]);
+ }
+#endif
+ }
+ /* build/process the config block, type == CONFIG (could be precomputed for each key) */
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+ Skein_Start_New_Type(ctx,CFG_FINAL);
+
+ memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+
+ Skein_Show_Key(512,&ctx->h,key,keyBytes);
+
+ /* compute the initial chaining values from config block */
+ Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->X are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt)
+{
+ size_t n;
+
+ Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* process full blocks, if any */
+ if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES)
+ {
+ if (ctx->h.bCnt) /* finish up any buffered message data */
+ {
+ n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */
+ if (n)
+ {
+ Skein_assert(n < msgByteCnt); /* check on our logic here */
+ memcpy(&ctx->b[ctx->h.bCnt],msg,n);
+ msgByteCnt -= n;
+ msg += n;
+ ctx->h.bCnt += n;
+ }
+ Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES);
+ Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES);
+ ctx->h.bCnt = 0;
+ }
+ /* now process any remaining full blocks, directly from input message data */
+ if (msgByteCnt > SKEIN_512_BLOCK_BYTES)
+ {
+ n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */
+ Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES);
+ msgByteCnt -= n * SKEIN_512_BLOCK_BYTES;
+ msg += n * SKEIN_512_BLOCK_BYTES;
+ }
+ Skein_assert(ctx->h.bCnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msgByteCnt)
+ {
+ Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt);
+ ctx->h.bCnt += msgByteCnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN_512_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt);
+
+ Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN_512_BLOCK_BYTES)
+ n = SKEIN_512_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*****************************************************************/
+/* 1024-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen)
+{
+ union
+ {
+ u08b_t b[SKEIN1024_STATE_BYTES];
+ u64b_t w[SKEIN1024_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+
+ switch (hashBitLen)
+ { /* use pre-computed values, where available */
+ case 512:
+ memcpy(ctx->X,SKEIN1024_IV_512 ,sizeof(ctx->X));
+ break;
+ case 384:
+ memcpy(ctx->X,SKEIN1024_IV_384 ,sizeof(ctx->X));
+ break;
+ case 1024:
+ memcpy(ctx->X,SKEIN1024_IV_1024,sizeof(ctx->X));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /* build/process the config block, type == CONFIG (could be precomputed) */
+ Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */
+
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */
+
+ /* compute the initial chaining values from config block */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */
+ Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+ break;
+ }
+
+ /* The chaining vars ctx->X are now initialized for the given hashBitLen. */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to Skein1024_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int Skein1024_InitExt(Skein1024_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes)
+{
+ union
+ {
+ u08b_t b[SKEIN1024_STATE_BYTES];
+ u64b_t w[SKEIN1024_STATE_WORDS];
+ } cfg; /* config block */
+
+ Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN);
+ Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->X[], based on key */
+ if (keyBytes == 0) /* is there a key? */
+ {
+ memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */
+ }
+ else /* here to pre-process a key */
+ {
+ Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X));
+ /* do a mini-Init right here */
+ ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */
+ Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */
+ memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */
+ Skein1024_Update(ctx,key,keyBytes); /* hash the key */
+ Skein1024_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */
+ memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */
+#if SKEIN_NEED_SWAP
+ {
+ uint_t i;
+ for (i=0;i<SKEIN1024_STATE_WORDS;i++) /* convert key bytes to context words */
+ ctx->X[i] = Skein_Swap64(ctx->X[i]);
+ }
+#endif
+ }
+ /* build/process the config block, type == CONFIG (could be precomputed for each key) */
+ ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
+ Skein_Start_New_Type(ctx,CFG_FINAL);
+
+ memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */
+ cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
+ cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
+ cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+
+ Skein_Show_Key(1024,&ctx->h,key,keyBytes);
+
+ /* compute the initial chaining values from config block */
+ Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->X are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ Skein_Start_New_Type(ctx,MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt)
+{
+ size_t n;
+
+ Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* process full blocks, if any */
+ if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES)
+ {
+ if (ctx->h.bCnt) /* finish up any buffered message data */
+ {
+ n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */
+ if (n)
+ {
+ Skein_assert(n < msgByteCnt); /* check on our logic here */
+ memcpy(&ctx->b[ctx->h.bCnt],msg,n);
+ msgByteCnt -= n;
+ msg += n;
+ ctx->h.bCnt += n;
+ }
+ Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES);
+ Skein1024_Process_Block(ctx,ctx->b,1,SKEIN1024_BLOCK_BYTES);
+ ctx->h.bCnt = 0;
+ }
+ /* now process any remaining full blocks, directly from input message data */
+ if (msgByteCnt > SKEIN1024_BLOCK_BYTES)
+ {
+ n = (msgByteCnt-1) / SKEIN1024_BLOCK_BYTES; /* number of full blocks to process */
+ Skein1024_Process_Block(ctx,msg,n,SKEIN1024_BLOCK_BYTES);
+ msgByteCnt -= n * SKEIN1024_BLOCK_BYTES;
+ msg += n * SKEIN1024_BLOCK_BYTES;
+ }
+ Skein_assert(ctx->h.bCnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msgByteCnt)
+ {
+ Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt);
+ ctx->h.bCnt += msgByteCnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int Skein1024_Final(Skein1024_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN1024_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt);
+
+ Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN1024_BLOCK_BYTES)
+ n = SKEIN1024_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(1024,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+
+/**************** Functions to support MAC/tree hashing ***************/
+/* (this code is identical for Optimized and Reference versions) */
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt);
+ Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_256_BLOCK_BYTES); /* "output" the state bytes */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt);
+ Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_512_BLOCK_BYTES); /* "output" the state bytes */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
+ if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */
+ memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt);
+ Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */
+
+ Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN1024_BLOCK_BYTES); /* "output" the state bytes */
+
+ return SKEIN_SUCCESS;
+}
+
+#if SKEIN_TREE_HASH
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int Skein_256_Output(Skein_256_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN_256_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN_256_BLOCK_BYTES)
+ n = SKEIN_256_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int Skein_512_Output(Skein_512_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN_512_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN_512_BLOCK_BYTES)
+ n = SKEIN_512_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int Skein1024_Output(Skein1024_Ctxt_t *ctx, u08b_t *hashVal)
+{
+ size_t i,n,byteCnt;
+ u64b_t X[SKEIN1024_STATE_WORDS];
+ Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */
+
+ /* now output the result */
+ byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */
+
+ /* run Threefish in "counter mode" to generate output */
+ memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */
+ memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */
+ for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++)
+ {
+ ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */
+ Skein_Start_New_Type(ctx,OUT_FINAL);
+ Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */
+ n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */
+ if (n >= SKEIN1024_BLOCK_BYTES)
+ n = SKEIN1024_BLOCK_BYTES;
+ Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */
+ Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES);
+ memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */
+ }
+ return SKEIN_SUCCESS;
+}
+#endif
diff --git a/srtp/crypto/skein.h b/srtp/crypto/skein.h new file mode 100644 index 0000000..345a112 --- /dev/null +++ b/srtp/crypto/skein.h @@ -0,0 +1,327 @@ +#ifndef _SKEIN_H_
+#define _SKEIN_H_ 1
+/**************************************************************************
+**
+** Interface declarations and internal definitions for Skein hashing.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+***************************************************************************
+**
+** The following compile-time switches may be defined to control some
+** tradeoffs between speed, code size, error checking, and security.
+**
+** The "default" note explains what happens when the switch is not defined.
+**
+** SKEIN_DEBUG -- make callouts from inside Skein code
+** to examine/display intermediate values.
+** [default: no callouts (no overhead)]
+**
+** SKEIN_ERR_CHECK -- how error checking is handled inside Skein
+** code. If not defined, most error checking
+** is disabled (for performance). Otherwise,
+** the switch value is interpreted as:
+** 0: use assert() to flag errors
+** 1: return SKEIN_FAIL to flag errors
+**
+***************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h> /* get size_t definition */
+#include <crypto/skein_port.h> /* get platform-specific definitions */
+
+enum
+ {
+ SKEIN_SUCCESS = 0, /* return codes from Skein calls */
+ SKEIN_FAIL = 1,
+ SKEIN_BAD_HASHLEN = 2
+ };
+
+#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */
+
+#define SKEIN_256_STATE_WORDS ( 4)
+#define SKEIN_512_STATE_WORDS ( 8)
+#define SKEIN1024_STATE_WORDS (16)
+#define SKEIN_MAX_STATE_WORDS (16)
+
+#define SKEIN_256_STATE_BYTES ( 8*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS)
+#define SKEIN1024_STATE_BYTES ( 8*SKEIN1024_STATE_WORDS)
+
+#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS)
+#define SKEIN1024_STATE_BITS (64*SKEIN1024_STATE_WORDS)
+
+#define SKEIN_256_BLOCK_BYTES ( 8*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS)
+#define SKEIN1024_BLOCK_BYTES ( 8*SKEIN1024_STATE_WORDS)
+
+typedef struct
+ {
+ size_t hashBitLen; /* size of hash result, in bits */
+ size_t bCnt; /* current byte count in buffer b[] */
+ u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */
+ } Skein_Ctxt_Hdr_t;
+
+typedef struct /* 256-bit Skein hash context structure */
+ {
+ Skein_Ctxt_Hdr_t h; /* common header context variables */
+ u64b_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */
+ u08b_t b[SKEIN_256_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */
+ } Skein_256_Ctxt_t;
+
+typedef struct /* 512-bit Skein hash context structure */
+ {
+ Skein_Ctxt_Hdr_t h; /* common header context variables */
+ u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */
+ u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */
+ } Skein_512_Ctxt_t;
+
+typedef struct /* 1024-bit Skein hash context structure */
+ {
+ Skein_Ctxt_Hdr_t h; /* common header context variables */
+ u64b_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */
+ u08b_t b[SKEIN1024_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */
+ } Skein1024_Ctxt_t;
+
+/* Skein APIs for (incremental) "straight hashing" */
+int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen);
+int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen);
+int Skein1024_Init (Skein1024_Ctxt_t *ctx, size_t hashBitLen);
+
+int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt);
+int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt);
+int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt);
+
+int Skein_256_Final (Skein_256_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein1024_Final (Skein1024_Ctxt_t *ctx, u08b_t * hashVal);
+
+/*
+** Skein APIs for "extended" initialization: MAC keys, tree hashing.
+** After an InitExt() call, just use Update/Final calls as with Init().
+**
+** Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes.
+** When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL,
+** the results of InitExt() are identical to calling Init().
+** The function Init() may be called once to "precompute" the IV for
+** a given hashBitLen value, then by saving a copy of the context
+** the IV computation may be avoided in later calls.
+** Similarly, the function InitExt() may be called once per MAC key
+** to precompute the MAC IV, then a copy of the context saved and
+** reused for each new MAC computation.
+**/
+int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes);
+int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes);
+int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes);
+
+/*
+** Skein APIs for MAC and tree hash:
+** Final_Pad: pad, do final block, but no OUTPUT type
+** Output: do just the output stage
+*/
+int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t * hashVal);
+
+#ifndef SKEIN_TREE_HASH
+#define SKEIN_TREE_HASH (1)
+#endif
+#if SKEIN_TREE_HASH
+int Skein_256_Output (Skein_256_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein_512_Output (Skein_512_Ctxt_t *ctx, u08b_t * hashVal);
+int Skein1024_Output (Skein1024_Ctxt_t *ctx, u08b_t * hashVal);
+#endif
+
+/*****************************************************************
+** "Internal" Skein definitions
+** -- not needed for sequential hashing API, but will be
+** helpful for other uses of Skein (e.g., tree hash mode).
+** -- included here so that they can be shared between
+** reference and optimized code.
+******************************************************************/
+
+/* tweak word T[1]: bit field starting positions */
+#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */
+
+#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */
+#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */
+#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */
+#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */
+#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */
+
+/* tweak word T[1]: flag bit definition(s) */
+#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST)
+#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL)
+#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD)
+
+/* tweak word T[1]: tree level bit field mask */
+#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL)
+#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL)
+
+/* tweak word T[1]: block type field */
+#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */
+#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */
+#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */
+#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */
+#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */
+#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */
+#define SKEIN_BLK_TYPE_MSG (48) /* message processing */
+#define SKEIN_BLK_TYPE_OUT (63) /* output stage */
+#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */
+
+#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE)
+#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */
+#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */
+#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */
+#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */
+#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */
+#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */
+#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */
+#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */
+#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */
+
+#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL)
+#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL)
+
+#define SKEIN_VERSION (1)
+
+#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */
+#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/
+#endif
+
+#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32))
+#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE)
+#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22)
+
+#define SKEIN_CFG_STR_LEN (4*8)
+
+/* bit field definitions in config block treeInfo word */
+#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0)
+#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8)
+#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16)
+
+#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS)
+#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS)
+#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS)
+
+#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \
+ ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \
+ (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \
+ (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) )
+
+#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */
+
+/*
+** Skein macros for getting/setting tweak words, etc.
+** These are useful for partial input bytes, hash tree init/update, etc.
+**/
+#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM])
+#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);}
+
+#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0)
+#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1)
+#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0)
+#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1)
+
+/* set both tweak words at once */
+#define Skein_Set_T0_T1(ctxPtr,T0,T1) \
+ { \
+ Skein_Set_T0(ctxPtr,(T0)); \
+ Skein_Set_T1(ctxPtr,(T1)); \
+ }
+
+#define Skein_Set_Type(ctxPtr,BLK_TYPE) \
+ Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE)
+
+/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */
+#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \
+ { Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; }
+
+#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; }
+#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; }
+
+#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);}
+
+/*****************************************************************
+** "Internal" Skein definitions for debugging and error checking
+******************************************************************/
+#ifdef SKEIN_DEBUG /* examine/display intermediate values? */
+#include "skein_debug.h"
+#else /* default is no callouts */
+#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr)
+#define Skein_Show_Round(bits,ctx,r,X)
+#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr)
+#define Skein_Show_Final(bits,ctx,cnt,outPtr)
+#define Skein_Show_Key(bits,ctx,key,keyBytes)
+#endif
+
+#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */
+#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */
+#define Skein_assert(x)
+#elif defined(SKEIN_ASSERT)
+#include <assert.h>
+#define Skein_Assert(x,retCode) assert(x)
+#define Skein_assert(x) assert(x)
+#else
+#include <assert.h>
+#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */
+#define Skein_assert(x) assert(x) /* internal error */
+#endif
+
+/*****************************************************************
+** Skein block function constants (shared across Ref and Opt code)
+******************************************************************/
+enum
+ {
+ /* Skein_256 round rotation constants */
+ R_256_0_0=14, R_256_0_1=16,
+ R_256_1_0=52, R_256_1_1=57,
+ R_256_2_0=23, R_256_2_1=40,
+ R_256_3_0= 5, R_256_3_1=37,
+ R_256_4_0=25, R_256_4_1=33,
+ R_256_5_0=46, R_256_5_1=12,
+ R_256_6_0=58, R_256_6_1=22,
+ R_256_7_0=32, R_256_7_1=32,
+
+ /* Skein_512 round rotation constants */
+ R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37,
+ R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42,
+ R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39,
+ R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56,
+ R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24,
+ R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17,
+ R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43,
+ R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22,
+
+ /* Skein1024 round rotation constants */
+ R1024_0_0=24, R1024_0_1=13, R1024_0_2= 8, R1024_0_3=47, R1024_0_4= 8, R1024_0_5=17, R1024_0_6=22, R1024_0_7=37,
+ R1024_1_0=38, R1024_1_1=19, R1024_1_2=10, R1024_1_3=55, R1024_1_4=49, R1024_1_5=18, R1024_1_6=23, R1024_1_7=52,
+ R1024_2_0=33, R1024_2_1= 4, R1024_2_2=51, R1024_2_3=13, R1024_2_4=34, R1024_2_5=41, R1024_2_6=59, R1024_2_7=17,
+ R1024_3_0= 5, R1024_3_1=20, R1024_3_2=48, R1024_3_3=41, R1024_3_4=47, R1024_3_5=28, R1024_3_6=16, R1024_3_7=25,
+ R1024_4_0=41, R1024_4_1= 9, R1024_4_2=37, R1024_4_3=31, R1024_4_4=12, R1024_4_5=47, R1024_4_6=44, R1024_4_7=30,
+ R1024_5_0=16, R1024_5_1=34, R1024_5_2=56, R1024_5_3=51, R1024_5_4= 4, R1024_5_5=53, R1024_5_6=42, R1024_5_7=41,
+ R1024_6_0=31, R1024_6_1=44, R1024_6_2=47, R1024_6_3=46, R1024_6_4=19, R1024_6_5=42, R1024_6_6=44, R1024_6_7=25,
+ R1024_7_0= 9, R1024_7_1=48, R1024_7_2=35, R1024_7_3=52, R1024_7_4=23, R1024_7_5=31, R1024_7_6=37, R1024_7_7=20
+ };
+
+#ifndef SKEIN_ROUNDS
+#define SKEIN_256_ROUNDS_TOTAL (72) /* number of rounds for the different block sizes */
+#define SKEIN_512_ROUNDS_TOTAL (72)
+#define SKEIN1024_ROUNDS_TOTAL (80)
+#else /* allow command-line define in range 8*(5..14) */
+#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5))
+#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5))
+#define SKEIN1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS ) + 5) % 10) + 5))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ifndef _SKEIN_H_ */
diff --git a/srtp/crypto/skeinApi.c b/srtp/crypto/skeinApi.c new file mode 100644 index 0000000..84f0120 --- /dev/null +++ b/srtp/crypto/skeinApi.c @@ -0,0 +1,225 @@ +/* +Copyright (c) 2010 Werner Dittmann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#define SKEIN_ERR_CHECK 1 +#include <crypto/skeinApi.h> +#include <string.h> +#include <stdio.h> + +int skeinCtxPrepare(SkeinCtx_t* ctx, SkeinSize_t size) +{ + Skein_Assert(ctx && size, SKEIN_FAIL); + + memset(ctx ,0, sizeof(SkeinCtx_t)); + ctx->skeinSize = size; + + return SKEIN_SUCCESS; +} + +int skeinInit(SkeinCtx_t* ctx, size_t hashBitLen) +{ + int ret = SKEIN_FAIL; + size_t Xlen = 0; + u64b_t* X = NULL; + uint64_t treeInfo = SKEIN_CFG_TREE_INFO_SEQUENTIAL; + + Skein_Assert(ctx, SKEIN_FAIL); + /* + * The following two lines rely of the fact that the real Skein contexts are + * a union in out context and thus have tha maximum memory available. + * The beauty of C :-) . + */ + X = ctx->m.s256.X; + Xlen = (size_t)(ctx->skeinSize/8); + /* + * If size is the same and hash bit length is zero then reuse + * the save chaining variables. + */ + switch (ctx->skeinSize) { + case Skein256: + ret = Skein_256_InitExt(&ctx->m.s256, hashBitLen, + treeInfo, NULL, 0); + break; + case Skein512: + ret = Skein_512_InitExt(&ctx->m.s512, hashBitLen, + treeInfo, NULL, 0); + break; + case Skein1024: + ret = Skein1024_InitExt(&ctx->m.s1024, hashBitLen, + treeInfo, NULL, 0); + break; + } + + if (ret == SKEIN_SUCCESS) { + /* Save chaining variables for this combination of size and hashBitLen */ + memcpy(ctx->XSave, X, Xlen); + } + return ret; +} + +int skeinMacInit(SkeinCtx_t* ctx, const uint8_t *key, size_t keyLen, + size_t hashBitLen) +{ + int ret = SKEIN_FAIL; + u64b_t* X = NULL; + size_t Xlen = 0; + uint64_t treeInfo = SKEIN_CFG_TREE_INFO_SEQUENTIAL; + + Skein_Assert(ctx, SKEIN_FAIL); + + X = ctx->m.s256.X; + Xlen = (size_t)(ctx->skeinSize/8); + + Skein_Assert(hashBitLen, SKEIN_BAD_HASHLEN); + + switch (ctx->skeinSize) { + case Skein256: + ret = Skein_256_InitExt(&ctx->m.s256, hashBitLen, + treeInfo, + (const u08b_t*)key, keyLen); + + break; + case Skein512: + ret = Skein_512_InitExt(&ctx->m.s512, hashBitLen, + treeInfo, + (const u08b_t*)key, keyLen); + break; + case Skein1024: + ret = Skein1024_InitExt(&ctx->m.s1024, hashBitLen, + treeInfo, + (const u08b_t*)key, keyLen); + + break; + } + if (ret == SKEIN_SUCCESS) { + /* Save chaining variables for this combination of key, keyLen, hashBitLen */ + memcpy(ctx->XSave, X, Xlen); + } + return ret; +} + +void skeinReset(SkeinCtx_t* ctx) +{ + size_t Xlen = 0; + u64b_t* X = NULL; + + /* + * The following two lines rely of the fact that the real Skein contexts are + * a union in out context and thus have tha maximum memory available. + * The beautiy of C :-) . + */ + X = ctx->m.s256.X; + Xlen = (size_t)(ctx->skeinSize/8); + /* + * If size is the same and hash bit length is zero then reuse + * the save chaining variables. + */ + /* Restore the chaing variable, reset byte counter */ + memcpy(X, ctx->XSave, Xlen); + + /* Setup context to process the message */ + Skein_Start_New_Type(&ctx->m, MSG); +} + +int skeinUpdate(SkeinCtx_t *ctx, const uint8_t *msg, + size_t msgByteCnt) +{ + int ret = SKEIN_FAIL; + Skein_Assert(ctx, SKEIN_FAIL); + + switch (ctx->skeinSize) { + case Skein256: + ret = Skein_256_Update(&ctx->m.s256, (const u08b_t*)msg, msgByteCnt); + break; + case Skein512: + ret = Skein_512_Update(&ctx->m.s512, (const u08b_t*)msg, msgByteCnt); + break; + case Skein1024: + ret = Skein1024_Update(&ctx->m.s1024, (const u08b_t*)msg, msgByteCnt); + break; + } + return ret; + +} + +int skeinUpdateBits(SkeinCtx_t *ctx, const uint8_t *msg, + size_t msgBitCnt) +{ + /* + * I've used the bit pad implementation from skein_test.c (see NIST CD) + * and modified it to use the convenience functions and added some pointer + * arithmetic. + */ + size_t length; + uint8_t mask; + uint8_t* up; + + /* only the final Update() call is allowed do partial bytes, else assert an error */ + Skein_Assert((ctx->m.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || msgBitCnt == 0, SKEIN_FAIL); + + /* if number of bits is a multiple of bytes - that's easy */ + if ((msgBitCnt & 0x7) == 0) { + return skeinUpdate(ctx, msg, msgBitCnt >> 3); + } + skeinUpdate(ctx, msg, (msgBitCnt >> 3) + 1); + + /* + * The next line rely on the fact that the real Skein contexts + * are a union in our context. After the addition the pointer points to + * Skein's real partial block buffer. + * If this layout ever changes we have to adapt this as well. + */ + up = (uint8_t*)ctx->m.s256.X + ctx->skeinSize / 8; + + Skein_Set_Bit_Pad_Flag(ctx->m.h); /* set tweak flag for the skeinFinal call */ + + /* now "pad" the final partial byte the way NIST likes */ + length = ctx->m.h.bCnt; /* get the bCnt value (same location for all block sizes) */ + Skein_assert(length != 0); /* internal sanity check: there IS a partial byte in the buffer! */ + mask = (uint8_t) (1u << (7 - (msgBitCnt & 7))); /* partial byte bit mask */ + up[length-1] = (uint8_t)((up[length-1] & (0-mask))|mask); /* apply bit padding on final byte (in the buffer) */ + + return SKEIN_SUCCESS; +} + +int skeinFinal(SkeinCtx_t* ctx, uint8_t* hash) +{ + int ret = SKEIN_FAIL; + Skein_Assert(ctx, SKEIN_FAIL); + + switch (ctx->skeinSize) { + case Skein256: + ret = Skein_256_Final(&ctx->m.s256, (u08b_t*)hash); + break; + case Skein512: + ret = Skein_512_Final(&ctx->m.s512, (u08b_t*)hash); + break; + case Skein1024: + ret = Skein1024_Final(&ctx->m.s1024, (u08b_t*)hash); + break; + } + return ret; +} diff --git a/srtp/crypto/skeinApi.h b/srtp/crypto/skeinApi.h new file mode 100644 index 0000000..2f25073 --- /dev/null +++ b/srtp/crypto/skeinApi.h @@ -0,0 +1,253 @@ +/* +Copyright (c) 2010 Werner Dittmann + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef SKEINAPI_H +#define SKEINAPI_H + +/** + * @file skeinApi.h + * @brief A Skein API and its functions. + * @{ + * + * This API and the functions that implement this API simplify the usage + * of Skein. The design and the way to use the functions follow the openSSL + * design but at the same time take care of some Skein specific behaviour + * and possibilities. + * + * The functions enable applications to create a normal Skein hashes and + * message authentication codes (MAC). + * + * Using these functions is simple and straight forward: + * + * @code + * + * #include <skeinApi.h> + * + * ... + * SkeinCtx_t ctx; // a Skein hash or MAC context + * + * // prepare context, here for a Skein with a state size of 512 bits. + * skeinCtxPrepare(&ctx, Skein512); + * + * // Initialize the context to set the requested hash length in bits + * // here request a output hash size of 31 bits (Skein supports variable + * // output sizes even very strange sizes) + * skeinInit(&ctx, 31); + * + * // Now update Skein with any number of message bits. A function that + * // takes a number of bytes is also available. + * skeinUpdateBits(&ctx, message, msgLength); + * + * // Now get the result of the Skein hash. The output buffer must be + * // large enough to hold the request number of output bits. The application + * // may now extract the bits. + * skeinFinal(&ctx, result); + * ... + * @endcode + * + * An application may use @c skeinReset to reset a Skein context and use + * it for creation of another hash with the same Skein state size and output + * bit length. In this case the API implementation restores some internal + * internal state data and saves a full Skein initialization round. + * + * To create a MAC the application just uses @c skeinMacInit instead of + * @c skeinInit. All other functions calls remain the same. + * + */ + +#include <crypto/skein.h> + +#ifdef _MSC_VER +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include <stdint.h> +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + /** + * Which Skein size to use + */ + typedef enum SkeinSize { + Skein256 = 256, /*!< Skein with 256 bit state */ + Skein512 = 512, /*!< Skein with 512 bit state */ + Skein1024 = 1024 /*!< Skein with 1024 bit state */ + } SkeinSize_t; + + /** + * Context for Skein. + * + * This structure was setup with some know-how of the internal + * Skein structures, in particular ordering of header and size dependent + * variables. If Skein implementation changes this, then adapt these + * structures as well. + */ + typedef struct SkeinCtx { + u64b_t skeinSize; + u64b_t XSave[SKEIN_MAX_STATE_WORDS]; /* save area for state variables */ + union { + Skein_Ctxt_Hdr_t h; + Skein_256_Ctxt_t s256; + Skein_512_Ctxt_t s512; + Skein1024_Ctxt_t s1024; + } m; + } SkeinCtx_t; + + /** + * Prepare a Skein context. + * + * An application must call this function before it can use the Skein + * context. The functions clears memory and initializes size dependent + * variables. + * + * @param ctx + * Pointer to a Skein context. + * @param size + * Which Skein size to use. + * @return + * SKEIN_SUCESS of SKEIN_FAIL + */ + int skeinCtxPrepare(SkeinCtx_t* ctx, SkeinSize_t size); + + /** + * Initialize a Skein context. + * + * Initializes the context with this data and saves the resulting Skein + * state variables for further use. + * + * @param ctx + * Pointer to a Skein context. + * @param hashBitLen + * Number of MAC hash bits to compute or zero + * @return + * SKEIN_SUCESS of SKEIN_FAIL + * @see skeinReset + */ + int skeinInit(SkeinCtx_t* ctx, size_t hashBitLen); + + /** + * Resets a Skein context for furter use. + * + * Restores the saved chaining variables to reset the Skein context. + * Thus applications can reuse the same setup to process several + * messages. This saves a complete Skein initialization cycle. + * + * @param ctx + * Pointer to a pre-initialized Skein MAC context + */ + void skeinReset(SkeinCtx_t* ctx); + + /** + * Initializes or reuses a Skein context for MAC usage. + * + * Initializes the context with this data and saves the resulting Skein + * state variables for further use. + * + * Applications call the normal Skein functions to update the MAC and + * get the final result. + * + * @param ctx + * Pointer to an empty or preinitialized Skein MAC context + * @param key + * Pointer to key bytes or NULL + * @param keyLen + * Length of the key in bytes or zero + * @param hashBitLen + * Number of MAC hash bits to compute or zero + * @return + * SKEIN_SUCESS of SKEIN_FAIL + */ + int skeinMacInit(SkeinCtx_t* ctx, const uint8_t *key, size_t keyLen, + size_t hashBitLen); + + /** + * Update Skein with the next part of the message. + * + * @param ctx + * Pointer to initialized Skein context + * @param msg + * Pointer to the message. + * @param msgByteCnt + * Length of the message in @b bytes + * @return + * Success or error code. + */ + int skeinUpdate(SkeinCtx_t *ctx, const uint8_t *msg, + size_t msgByteCnt); + + /** + * Update the hash with a message bit string. + * + * Skein can handle data not only as bytes but also as bit strings of + * arbitrary length (up to its maximum design size). + * + * @param ctx + * Pointer to initialized Skein context + * @param msg + * Pointer to the message. + * @param msgBitCnt + * Length of the message in @b bits. + */ + int skeinUpdateBits(SkeinCtx_t *ctx, const uint8_t *msg, + size_t msgBitCnt); + + /** + * Finalize Skein and return the hash. + * + * Before an application can reuse a Skein setup the application must + * reinitialize the Skein context.See the approriate initialization + * methods how to achieve this. + * + * @param ctx + * Pointer to initialized Skein context + * @param hash + * Pointer to buffer that receives the hash. The buffer must be large + * enough to store @c hashBitLen bits. + * @return + * Success or error code. + * @see skeinInit + * @see skeinMacInit + */ + int skeinFinal(SkeinCtx_t* ctx, uint8_t* hash); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ +#endif diff --git a/srtp/crypto/skein_block.c b/srtp/crypto/skein_block.c new file mode 100644 index 0000000..fbf37e7 --- /dev/null +++ b/srtp/crypto/skein_block.c @@ -0,0 +1,689 @@ +/***********************************************************************
+**
+** Implementation of the Skein block functions.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+** Compile-time switches:
+**
+** SKEIN_USE_ASM -- set bits (256/512/1024) to select which
+** versions use ASM code for block processing
+** [default: use C for all block sizes]
+**
+************************************************************************/
+
+#include <string.h>
+#include <crypto/skein.h>
+
+#ifndef SKEIN_USE_ASM
+#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */
+#endif
+
+#ifndef SKEIN_LOOP
+#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */
+#endif
+
+#define BLK_BITS (WCNT*64) /* some useful definitions for code here */
+#define KW_TWK_BASE (0)
+#define KW_KEY_BASE (3)
+#define ks (kw + KW_KEY_BASE)
+#define ts (kw + KW_TWK_BASE)
+
+#ifdef SKEIN_DEBUG
+#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; }
+#else
+#define DebugSaveTweak(ctx)
+#endif
+
+/***************************** Skein_256 ******************************/
+#if !(SKEIN_USE_ASM & 256)
+void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd)
+ { /* do it in C */
+ enum
+ {
+ WCNT = SKEIN_256_STATE_WORDS
+ };
+#undef RCNT
+#define RCNT (SKEIN_256_ROUNDS_TOTAL/8)
+
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_256 (((SKEIN_LOOP)/100)%10)
+#else
+#define SKEIN_UNROLL_256 (0)
+#endif
+
+#if SKEIN_UNROLL_256
+#if (RCNT % SKEIN_UNROLL_256)
+#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */
+#endif
+ size_t r;
+ u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/
+#else
+ u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */
+#endif
+ u64b_t X0,X1,X2,X3; /* local copy of context vars, for speed */
+ u64b_t w [WCNT]; /* local copy of input block */
+#ifdef SKEIN_DEBUG
+ const u64b_t *Xptr[4]; /* use for debugging (help compiler put Xn in registers) */
+ Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3;
+#endif
+ Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
+ ts[0] = ctx->h.T[0];
+ ts[1] = ctx->h.T[1];
+ do {
+ /* this implementation only supports 2**64 input bytes (no carry out here) */
+ ts[0] += byteCntAdd; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[0] = ctx->X[0];
+ ks[1] = ctx->X[1];
+ ks[2] = ctx->X[2];
+ ks[3] = ctx->X[3];
+ ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */
+ DebugSaveTweak(ctx);
+ Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts);
+
+ X0 = w[0] + ks[0]; /* do the first full key injection */
+ X1 = w[1] + ks[1] + ts[0];
+ X2 = w[2] + ks[2] + ts[1];
+ X3 = w[3] + ks[3];
+
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); /* show starting state values */
+
+ blkPtr += SKEIN_256_BLOCK_BYTES;
+
+ /* run the rounds */
+
+#define Round256(p0,p1,p2,p3,ROT,rNum) \
+ X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \
+ X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \
+
+#if SKEIN_UNROLL_256 == 0
+#define R256(p0,p1,p2,p3,ROT,rNum) /* fully unrolled */ \
+ Round256(p0,p1,p2,p3,ROT,rNum) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr);
+
+#define I256(R) \
+ X0 += ks[((R)+1) % 5]; /* inject the key schedule value */ \
+ X1 += ks[((R)+2) % 5] + ts[((R)+1) % 3]; \
+ X2 += ks[((R)+3) % 5] + ts[((R)+2) % 3]; \
+ X3 += ks[((R)+4) % 5] + (R)+1; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+#else /* looping version */
+#define R256(p0,p1,p2,p3,ROT,rNum) \
+ Round256(p0,p1,p2,p3,ROT,rNum) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr);
+
+#define I256(R) \
+ X0 += ks[r+(R)+0]; /* inject the key schedule value */ \
+ X1 += ks[r+(R)+1] + ts[r+(R)+0]; \
+ X2 += ks[r+(R)+2] + ts[r+(R)+1]; \
+ X3 += ks[r+(R)+3] + r+(R) ; \
+ ks[r + (R)+4 ] = ks[r+(R)-1]; /* rotate key schedule */\
+ ts[r + (R)+2 ] = ts[r+(R)-1]; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+
+ for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_256) /* loop thru it */
+#endif
+ {
+#define R256_8_rounds(R) \
+ R256(0,1,2,3,R_256_0,8*(R) + 1); \
+ R256(0,3,2,1,R_256_1,8*(R) + 2); \
+ R256(0,1,2,3,R_256_2,8*(R) + 3); \
+ R256(0,3,2,1,R_256_3,8*(R) + 4); \
+ I256(2*(R)); \
+ R256(0,1,2,3,R_256_4,8*(R) + 5); \
+ R256(0,3,2,1,R_256_5,8*(R) + 6); \
+ R256(0,1,2,3,R_256_6,8*(R) + 7); \
+ R256(0,3,2,1,R_256_7,8*(R) + 8); \
+ I256(2*(R)+1);
+
+ R256_8_rounds( 0);
+
+#define R256_Unroll_R(NN) ((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_256 > (NN)))
+
+ #if R256_Unroll_R( 1)
+ R256_8_rounds( 1);
+ #endif
+ #if R256_Unroll_R( 2)
+ R256_8_rounds( 2);
+ #endif
+ #if R256_Unroll_R( 3)
+ R256_8_rounds( 3);
+ #endif
+ #if R256_Unroll_R( 4)
+ R256_8_rounds( 4);
+ #endif
+ #if R256_Unroll_R( 5)
+ R256_8_rounds( 5);
+ #endif
+ #if R256_Unroll_R( 6)
+ R256_8_rounds( 6);
+ #endif
+ #if R256_Unroll_R( 7)
+ R256_8_rounds( 7);
+ #endif
+ #if R256_Unroll_R( 8)
+ R256_8_rounds( 8);
+ #endif
+ #if R256_Unroll_R( 9)
+ R256_8_rounds( 9);
+ #endif
+ #if R256_Unroll_R(10)
+ R256_8_rounds(10);
+ #endif
+ #if R256_Unroll_R(11)
+ R256_8_rounds(11);
+ #endif
+ #if R256_Unroll_R(12)
+ R256_8_rounds(12);
+ #endif
+ #if R256_Unroll_R(13)
+ R256_8_rounds(13);
+ #endif
+ #if R256_Unroll_R(14)
+ R256_8_rounds(14);
+ #endif
+ #if (SKEIN_UNROLL_256 > 14)
+#error "need more unrolling in Skein_256_Process_Block"
+ #endif
+ }
+ /* do the final "feedforward" xor, update context chaining vars */
+ ctx->X[0] = X0 ^ w[0];
+ ctx->X[1] = X1 ^ w[1];
+ ctx->X[2] = X2 ^ w[2];
+ ctx->X[3] = X3 ^ w[3];
+
+ Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X);
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ }
+ while (--blkCnt);
+ ctx->h.T[0] = ts[0];
+ ctx->h.T[1] = ts[1];
+ }
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t Skein_256_Process_Block_CodeSize(void)
+ {
+ return ((u08b_t *) Skein_256_Process_Block_CodeSize) -
+ ((u08b_t *) Skein_256_Process_Block);
+ }
+uint_t Skein_256_Unroll_Cnt(void)
+ {
+ return SKEIN_UNROLL_256;
+ }
+#endif
+#endif
+
+/***************************** Skein_512 ******************************/
+#if !(SKEIN_USE_ASM & 512)
+void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd)
+ { /* do it in C */
+ enum
+ {
+ WCNT = SKEIN_512_STATE_WORDS
+ };
+#undef RCNT
+#define RCNT (SKEIN_512_ROUNDS_TOTAL/8)
+
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10)
+#else
+#define SKEIN_UNROLL_512 (0)
+#endif
+
+#if SKEIN_UNROLL_512
+#if (RCNT % SKEIN_UNROLL_512)
+#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */
+#endif
+ size_t r;
+ u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/
+#else
+ u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */
+#endif
+ u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */
+ u64b_t w [WCNT]; /* local copy of input block */
+#ifdef SKEIN_DEBUG
+ const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */
+ Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3;
+ Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7;
+#endif
+
+ Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
+ ts[0] = ctx->h.T[0];
+ ts[1] = ctx->h.T[1];
+ do {
+ /* this implementation only supports 2**64 input bytes (no carry out here) */
+ ts[0] += byteCntAdd; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[0] = ctx->X[0];
+ ks[1] = ctx->X[1];
+ ks[2] = ctx->X[2];
+ ks[3] = ctx->X[3];
+ ks[4] = ctx->X[4];
+ ks[5] = ctx->X[5];
+ ks[6] = ctx->X[6];
+ ks[7] = ctx->X[7];
+ ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^
+ ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */
+ DebugSaveTweak(ctx);
+ Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts);
+
+ X0 = w[0] + ks[0]; /* do the first full key injection */
+ X1 = w[1] + ks[1];
+ X2 = w[2] + ks[2];
+ X3 = w[3] + ks[3];
+ X4 = w[4] + ks[4];
+ X5 = w[5] + ks[5] + ts[0];
+ X6 = w[6] + ks[6] + ts[1];
+ X7 = w[7] + ks[7];
+
+ blkPtr += SKEIN_512_BLOCK_BYTES;
+
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr);
+ /* run the rounds */
+#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \
+ X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \
+ X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \
+ X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \
+ X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \
+
+#if SKEIN_UNROLL_512 == 0
+#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \
+ Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr);
+
+#define I512(R) \
+ X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \
+ X1 += ks[((R)+2) % 9]; \
+ X2 += ks[((R)+3) % 9]; \
+ X3 += ks[((R)+4) % 9]; \
+ X4 += ks[((R)+5) % 9]; \
+ X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \
+ X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \
+ X7 += ks[((R)+8) % 9] + (R)+1; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+#else /* looping version */
+#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \
+ Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr);
+
+#define I512(R) \
+ X0 += ks[r+(R)+0]; /* inject the key schedule value */ \
+ X1 += ks[r+(R)+1]; \
+ X2 += ks[r+(R)+2]; \
+ X3 += ks[r+(R)+3]; \
+ X4 += ks[r+(R)+4]; \
+ X5 += ks[r+(R)+5] + ts[r+(R)+0]; \
+ X6 += ks[r+(R)+6] + ts[r+(R)+1]; \
+ X7 += ks[r+(R)+7] + r+(R) ; \
+ ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \
+ ts[r + (R)+2] = ts[r+(R)-1]; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+
+ for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */
+#endif /* end of looped code definitions */
+ {
+#define R512_8_rounds(R) /* do 8 full rounds */ \
+ R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \
+ R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \
+ R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \
+ R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \
+ I512(2*(R)); \
+ R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \
+ R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \
+ R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \
+ R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \
+ I512(2*(R)+1); /* and key injection */
+
+ R512_8_rounds( 0);
+
+#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN)))
+
+ #if R512_Unroll_R( 1)
+ R512_8_rounds( 1);
+ #endif
+ #if R512_Unroll_R( 2)
+ R512_8_rounds( 2);
+ #endif
+ #if R512_Unroll_R( 3)
+ R512_8_rounds( 3);
+ #endif
+ #if R512_Unroll_R( 4)
+ R512_8_rounds( 4);
+ #endif
+ #if R512_Unroll_R( 5)
+ R512_8_rounds( 5);
+ #endif
+ #if R512_Unroll_R( 6)
+ R512_8_rounds( 6);
+ #endif
+ #if R512_Unroll_R( 7)
+ R512_8_rounds( 7);
+ #endif
+ #if R512_Unroll_R( 8)
+ R512_8_rounds( 8);
+ #endif
+ #if R512_Unroll_R( 9)
+ R512_8_rounds( 9);
+ #endif
+ #if R512_Unroll_R(10)
+ R512_8_rounds(10);
+ #endif
+ #if R512_Unroll_R(11)
+ R512_8_rounds(11);
+ #endif
+ #if R512_Unroll_R(12)
+ R512_8_rounds(12);
+ #endif
+ #if R512_Unroll_R(13)
+ R512_8_rounds(13);
+ #endif
+ #if R512_Unroll_R(14)
+ R512_8_rounds(14);
+ #endif
+ #if (SKEIN_UNROLL_512 > 14)
+#error "need more unrolling in Skein_512_Process_Block"
+ #endif
+ }
+
+ /* do the final "feedforward" xor, update context chaining vars */
+ ctx->X[0] = X0 ^ w[0];
+ ctx->X[1] = X1 ^ w[1];
+ ctx->X[2] = X2 ^ w[2];
+ ctx->X[3] = X3 ^ w[3];
+ ctx->X[4] = X4 ^ w[4];
+ ctx->X[5] = X5 ^ w[5];
+ ctx->X[6] = X6 ^ w[6];
+ ctx->X[7] = X7 ^ w[7];
+ Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X);
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ }
+ while (--blkCnt);
+ ctx->h.T[0] = ts[0];
+ ctx->h.T[1] = ts[1];
+ }
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t Skein_512_Process_Block_CodeSize(void)
+ {
+ return ((u08b_t *) Skein_512_Process_Block_CodeSize) -
+ ((u08b_t *) Skein_512_Process_Block);
+ }
+uint_t Skein_512_Unroll_Cnt(void)
+ {
+ return SKEIN_UNROLL_512;
+ }
+#endif
+#endif
+
+/***************************** Skein1024 ******************************/
+#if !(SKEIN_USE_ASM & 1024)
+void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd)
+ { /* do it in C, always looping (unrolled is bigger AND slower!) */
+ enum
+ {
+ WCNT = SKEIN1024_STATE_WORDS
+ };
+#undef RCNT
+#define RCNT (SKEIN1024_ROUNDS_TOTAL/8)
+
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10)
+#else
+#define SKEIN_UNROLL_1024 (0)
+#endif
+
+#if (SKEIN_UNROLL_1024 != 0)
+#if (RCNT % SKEIN_UNROLL_1024)
+#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */
+#endif
+ size_t r;
+ u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/
+#else
+ u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */
+#endif
+
+ u64b_t X00,X01,X02,X03,X04,X05,X06,X07, /* local copy of vars, for speed */
+ X08,X09,X10,X11,X12,X13,X14,X15;
+ u64b_t w [WCNT]; /* local copy of input block */
+#ifdef SKEIN_DEBUG
+ const u64b_t *Xptr[16]; /* use for debugging (help compiler put Xn in registers) */
+ Xptr[ 0] = &X00; Xptr[ 1] = &X01; Xptr[ 2] = &X02; Xptr[ 3] = &X03;
+ Xptr[ 4] = &X04; Xptr[ 5] = &X05; Xptr[ 6] = &X06; Xptr[ 7] = &X07;
+ Xptr[ 8] = &X08; Xptr[ 9] = &X09; Xptr[10] = &X10; Xptr[11] = &X11;
+ Xptr[12] = &X12; Xptr[13] = &X13; Xptr[14] = &X14; Xptr[15] = &X15;
+#endif
+
+ Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
+ ts[0] = ctx->h.T[0];
+ ts[1] = ctx->h.T[1];
+ do {
+ /* this implementation only supports 2**64 input bytes (no carry out here) */
+ ts[0] += byteCntAdd; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[ 0] = ctx->X[ 0];
+ ks[ 1] = ctx->X[ 1];
+ ks[ 2] = ctx->X[ 2];
+ ks[ 3] = ctx->X[ 3];
+ ks[ 4] = ctx->X[ 4];
+ ks[ 5] = ctx->X[ 5];
+ ks[ 6] = ctx->X[ 6];
+ ks[ 7] = ctx->X[ 7];
+ ks[ 8] = ctx->X[ 8];
+ ks[ 9] = ctx->X[ 9];
+ ks[10] = ctx->X[10];
+ ks[11] = ctx->X[11];
+ ks[12] = ctx->X[12];
+ ks[13] = ctx->X[13];
+ ks[14] = ctx->X[14];
+ ks[15] = ctx->X[15];
+ ks[16] = ks[ 0] ^ ks[ 1] ^ ks[ 2] ^ ks[ 3] ^
+ ks[ 4] ^ ks[ 5] ^ ks[ 6] ^ ks[ 7] ^
+ ks[ 8] ^ ks[ 9] ^ ks[10] ^ ks[11] ^
+ ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */
+ DebugSaveTweak(ctx);
+ Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts);
+
+ X00 = w[ 0] + ks[ 0]; /* do the first full key injection */
+ X01 = w[ 1] + ks[ 1];
+ X02 = w[ 2] + ks[ 2];
+ X03 = w[ 3] + ks[ 3];
+ X04 = w[ 4] + ks[ 4];
+ X05 = w[ 5] + ks[ 5];
+ X06 = w[ 6] + ks[ 6];
+ X07 = w[ 7] + ks[ 7];
+ X08 = w[ 8] + ks[ 8];
+ X09 = w[ 9] + ks[ 9];
+ X10 = w[10] + ks[10];
+ X11 = w[11] + ks[11];
+ X12 = w[12] + ks[12];
+ X13 = w[13] + ks[13] + ts[0];
+ X14 = w[14] + ks[14] + ts[1];
+ X15 = w[15] + ks[15];
+
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr);
+
+#define Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rNum) \
+ X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \
+ X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \
+ X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \
+ X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \
+ X##p8 += X##p9; X##p9 = RotL_64(X##p9,ROT##_4); X##p9 ^= X##p8; \
+ X##pA += X##pB; X##pB = RotL_64(X##pB,ROT##_5); X##pB ^= X##pA; \
+ X##pC += X##pD; X##pD = RotL_64(X##pD,ROT##_6); X##pD ^= X##pC; \
+ X##pE += X##pF; X##pF = RotL_64(X##pF,ROT##_7); X##pF ^= X##pE; \
+
+#if SKEIN_UNROLL_1024 == 0
+#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \
+ Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rn,Xptr);
+
+#define I1024(R) \
+ X00 += ks[((R)+ 1) % 17]; /* inject the key schedule value */ \
+ X01 += ks[((R)+ 2) % 17]; \
+ X02 += ks[((R)+ 3) % 17]; \
+ X03 += ks[((R)+ 4) % 17]; \
+ X04 += ks[((R)+ 5) % 17]; \
+ X05 += ks[((R)+ 6) % 17]; \
+ X06 += ks[((R)+ 7) % 17]; \
+ X07 += ks[((R)+ 8) % 17]; \
+ X08 += ks[((R)+ 9) % 17]; \
+ X09 += ks[((R)+10) % 17]; \
+ X10 += ks[((R)+11) % 17]; \
+ X11 += ks[((R)+12) % 17]; \
+ X12 += ks[((R)+13) % 17]; \
+ X13 += ks[((R)+14) % 17] + ts[((R)+1) % 3]; \
+ X14 += ks[((R)+15) % 17] + ts[((R)+2) % 3]; \
+ X15 += ks[((R)+16) % 17] + (R)+1; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+#else /* looping version */
+#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \
+ Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rn,Xptr);
+
+#define I1024(R) \
+ X00 += ks[r+(R)+ 0]; /* inject the key schedule value */ \
+ X01 += ks[r+(R)+ 1]; \
+ X02 += ks[r+(R)+ 2]; \
+ X03 += ks[r+(R)+ 3]; \
+ X04 += ks[r+(R)+ 4]; \
+ X05 += ks[r+(R)+ 5]; \
+ X06 += ks[r+(R)+ 6]; \
+ X07 += ks[r+(R)+ 7]; \
+ X08 += ks[r+(R)+ 8]; \
+ X09 += ks[r+(R)+ 9]; \
+ X10 += ks[r+(R)+10]; \
+ X11 += ks[r+(R)+11]; \
+ X12 += ks[r+(R)+12]; \
+ X13 += ks[r+(R)+13] + ts[r+(R)+0]; \
+ X14 += ks[r+(R)+14] + ts[r+(R)+1]; \
+ X15 += ks[r+(R)+15] + r+(R) ; \
+ ks[r + (R)+16] = ks[r+(R)-1]; /* rotate key schedule */ \
+ ts[r + (R)+ 2] = ts[r+(R)-1]; \
+ Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr);
+
+ for (r=1;r <= 2*RCNT;r+=2*SKEIN_UNROLL_1024) /* loop thru it */
+#endif
+ {
+#define R1024_8_rounds(R) /* do 8 full rounds */ \
+ R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_0,8*(R) + 1); \
+ R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_1,8*(R) + 2); \
+ R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_2,8*(R) + 3); \
+ R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_3,8*(R) + 4); \
+ I1024(2*(R)); \
+ R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_4,8*(R) + 5); \
+ R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_5,8*(R) + 6); \
+ R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_6,8*(R) + 7); \
+ R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_7,8*(R) + 8); \
+ I1024(2*(R)+1);
+
+ R1024_8_rounds( 0);
+
+#define R1024_Unroll_R(NN) ((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_1024 > (NN)))
+
+ #if R1024_Unroll_R( 1)
+ R1024_8_rounds( 1);
+ #endif
+ #if R1024_Unroll_R( 2)
+ R1024_8_rounds( 2);
+ #endif
+ #if R1024_Unroll_R( 3)
+ R1024_8_rounds( 3);
+ #endif
+ #if R1024_Unroll_R( 4)
+ R1024_8_rounds( 4);
+ #endif
+ #if R1024_Unroll_R( 5)
+ R1024_8_rounds( 5);
+ #endif
+ #if R1024_Unroll_R( 6)
+ R1024_8_rounds( 6);
+ #endif
+ #if R1024_Unroll_R( 7)
+ R1024_8_rounds( 7);
+ #endif
+ #if R1024_Unroll_R( 8)
+ R1024_8_rounds( 8);
+ #endif
+ #if R1024_Unroll_R( 9)
+ R1024_8_rounds( 9);
+ #endif
+ #if R1024_Unroll_R(10)
+ R1024_8_rounds(10);
+ #endif
+ #if R1024_Unroll_R(11)
+ R1024_8_rounds(11);
+ #endif
+ #if R1024_Unroll_R(12)
+ R1024_8_rounds(12);
+ #endif
+ #if R1024_Unroll_R(13)
+ R1024_8_rounds(13);
+ #endif
+ #if R1024_Unroll_R(14)
+ R1024_8_rounds(14);
+ #endif
+ #if (SKEIN_UNROLL_1024 > 14)
+#error "need more unrolling in Skein_1024_Process_Block"
+ #endif
+ }
+ /* do the final "feedforward" xor, update context chaining vars */
+
+ ctx->X[ 0] = X00 ^ w[ 0];
+ ctx->X[ 1] = X01 ^ w[ 1];
+ ctx->X[ 2] = X02 ^ w[ 2];
+ ctx->X[ 3] = X03 ^ w[ 3];
+ ctx->X[ 4] = X04 ^ w[ 4];
+ ctx->X[ 5] = X05 ^ w[ 5];
+ ctx->X[ 6] = X06 ^ w[ 6];
+ ctx->X[ 7] = X07 ^ w[ 7];
+ ctx->X[ 8] = X08 ^ w[ 8];
+ ctx->X[ 9] = X09 ^ w[ 9];
+ ctx->X[10] = X10 ^ w[10];
+ ctx->X[11] = X11 ^ w[11];
+ ctx->X[12] = X12 ^ w[12];
+ ctx->X[13] = X13 ^ w[13];
+ ctx->X[14] = X14 ^ w[14];
+ ctx->X[15] = X15 ^ w[15];
+
+ Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X);
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ blkPtr += SKEIN1024_BLOCK_BYTES;
+ }
+ while (--blkCnt);
+ ctx->h.T[0] = ts[0];
+ ctx->h.T[1] = ts[1];
+ }
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t Skein1024_Process_Block_CodeSize(void)
+ {
+ return ((u08b_t *) Skein1024_Process_Block_CodeSize) -
+ ((u08b_t *) Skein1024_Process_Block);
+ }
+uint_t Skein1024_Unroll_Cnt(void)
+ {
+ return SKEIN_UNROLL_1024;
+ }
+#endif
+#endif
diff --git a/srtp/crypto/skein_iv.h b/srtp/crypto/skein_iv.h new file mode 100644 index 0000000..0c62fac --- /dev/null +++ b/srtp/crypto/skein_iv.h @@ -0,0 +1,199 @@ +#ifndef _SKEIN_IV_H_
+#define _SKEIN_IV_H_
+
+#include <crypto/skein.h> /* get Skein macros and types */
+
+/*
+***************** Pre-computed Skein IVs *******************
+**
+** NOTE: these values are not "magic" constants, but
+** are generated using the Threefish block function.
+** They are pre-computed here only for speed; i.e., to
+** avoid the need for a Threefish call during Init().
+**
+** The IV for any fixed hash length may be pre-computed.
+** Only the most common values are included here.
+**
+************************************************************
+**/
+
+#define MK_64 SKEIN_MK_64
+
+/* blkSize = 256 bits. hashSize = 128 bits */
+const u64b_t SKEIN_256_IV_128[] =
+ {
+ MK_64(0xE1111906,0x964D7260),
+ MK_64(0x883DAAA7,0x7C8D811C),
+ MK_64(0x10080DF4,0x91960F7A),
+ MK_64(0xCCF7DDE5,0xB45BC1C2)
+ };
+
+/* blkSize = 256 bits. hashSize = 160 bits */
+const u64b_t SKEIN_256_IV_160[] =
+ {
+ MK_64(0x14202314,0x72825E98),
+ MK_64(0x2AC4E9A2,0x5A77E590),
+ MK_64(0xD47A5856,0x8838D63E),
+ MK_64(0x2DD2E496,0x8586AB7D)
+ };
+
+/* blkSize = 256 bits. hashSize = 224 bits */
+const u64b_t SKEIN_256_IV_224[] =
+ {
+ MK_64(0xC6098A8C,0x9AE5EA0B),
+ MK_64(0x876D5686,0x08C5191C),
+ MK_64(0x99CB88D7,0xD7F53884),
+ MK_64(0x384BDDB1,0xAEDDB5DE)
+ };
+
+/* blkSize = 256 bits. hashSize = 256 bits */
+const u64b_t SKEIN_256_IV_256[] =
+ {
+ MK_64(0xFC9DA860,0xD048B449),
+ MK_64(0x2FCA6647,0x9FA7D833),
+ MK_64(0xB33BC389,0x6656840F),
+ MK_64(0x6A54E920,0xFDE8DA69)
+ };
+
+/* blkSize = 512 bits. hashSize = 128 bits */
+const u64b_t SKEIN_512_IV_128[] =
+ {
+ MK_64(0xA8BC7BF3,0x6FBF9F52),
+ MK_64(0x1E9872CE,0xBD1AF0AA),
+ MK_64(0x309B1790,0xB32190D3),
+ MK_64(0xBCFBB854,0x3F94805C),
+ MK_64(0x0DA61BCD,0x6E31B11B),
+ MK_64(0x1A18EBEA,0xD46A32E3),
+ MK_64(0xA2CC5B18,0xCE84AA82),
+ MK_64(0x6982AB28,0x9D46982D)
+ };
+
+/* blkSize = 512 bits. hashSize = 160 bits */
+const u64b_t SKEIN_512_IV_160[] =
+ {
+ MK_64(0x28B81A2A,0xE013BD91),
+ MK_64(0xC2F11668,0xB5BDF78F),
+ MK_64(0x1760D8F3,0xF6A56F12),
+ MK_64(0x4FB74758,0x8239904F),
+ MK_64(0x21EDE07F,0x7EAF5056),
+ MK_64(0xD908922E,0x63ED70B8),
+ MK_64(0xB8EC76FF,0xECCB52FA),
+ MK_64(0x01A47BB8,0xA3F27A6E)
+ };
+
+/* blkSize = 512 bits. hashSize = 224 bits */
+const u64b_t SKEIN_512_IV_224[] =
+ {
+ MK_64(0xCCD06162,0x48677224),
+ MK_64(0xCBA65CF3,0xA92339EF),
+ MK_64(0x8CCD69D6,0x52FF4B64),
+ MK_64(0x398AED7B,0x3AB890B4),
+ MK_64(0x0F59D1B1,0x457D2BD0),
+ MK_64(0x6776FE65,0x75D4EB3D),
+ MK_64(0x99FBC70E,0x997413E9),
+ MK_64(0x9E2CFCCF,0xE1C41EF7)
+ };
+
+/* blkSize = 512 bits. hashSize = 256 bits */
+const u64b_t SKEIN_512_IV_256[] =
+ {
+ MK_64(0xCCD044A1,0x2FDB3E13),
+ MK_64(0xE8359030,0x1A79A9EB),
+ MK_64(0x55AEA061,0x4F816E6F),
+ MK_64(0x2A2767A4,0xAE9B94DB),
+ MK_64(0xEC06025E,0x74DD7683),
+ MK_64(0xE7A436CD,0xC4746251),
+ MK_64(0xC36FBAF9,0x393AD185),
+ MK_64(0x3EEDBA18,0x33EDFC13)
+ };
+
+/* blkSize = 512 bits. hashSize = 384 bits */
+const u64b_t SKEIN_512_IV_384[] =
+ {
+ MK_64(0xA3F6C6BF,0x3A75EF5F),
+ MK_64(0xB0FEF9CC,0xFD84FAA4),
+ MK_64(0x9D77DD66,0x3D770CFE),
+ MK_64(0xD798CBF3,0xB468FDDA),
+ MK_64(0x1BC4A666,0x8A0E4465),
+ MK_64(0x7ED7D434,0xE5807407),
+ MK_64(0x548FC1AC,0xD4EC44D6),
+ MK_64(0x266E1754,0x6AA18FF8)
+ };
+
+/* blkSize = 512 bits. hashSize = 512 bits */
+const u64b_t SKEIN_512_IV_512[] =
+ {
+ MK_64(0x4903ADFF,0x749C51CE),
+ MK_64(0x0D95DE39,0x9746DF03),
+ MK_64(0x8FD19341,0x27C79BCE),
+ MK_64(0x9A255629,0xFF352CB1),
+ MK_64(0x5DB62599,0xDF6CA7B0),
+ MK_64(0xEABE394C,0xA9D5C3F4),
+ MK_64(0x991112C7,0x1A75B523),
+ MK_64(0xAE18A40B,0x660FCC33)
+ };
+
+/* blkSize = 1024 bits. hashSize = 384 bits */
+const u64b_t SKEIN1024_IV_384[] =
+ {
+ MK_64(0x5102B6B8,0xC1894A35),
+ MK_64(0xFEEBC9E3,0xFE8AF11A),
+ MK_64(0x0C807F06,0xE32BED71),
+ MK_64(0x60C13A52,0xB41A91F6),
+ MK_64(0x9716D35D,0xD4917C38),
+ MK_64(0xE780DF12,0x6FD31D3A),
+ MK_64(0x797846B6,0xC898303A),
+ MK_64(0xB172C2A8,0xB3572A3B),
+ MK_64(0xC9BC8203,0xA6104A6C),
+ MK_64(0x65909338,0xD75624F4),
+ MK_64(0x94BCC568,0x4B3F81A0),
+ MK_64(0x3EBBF51E,0x10ECFD46),
+ MK_64(0x2DF50F0B,0xEEB08542),
+ MK_64(0x3B5A6530,0x0DBC6516),
+ MK_64(0x484B9CD2,0x167BBCE1),
+ MK_64(0x2D136947,0xD4CBAFEA)
+ };
+
+/* blkSize = 1024 bits. hashSize = 512 bits */
+const u64b_t SKEIN1024_IV_512[] =
+ {
+ MK_64(0xCAEC0E5D,0x7C1B1B18),
+ MK_64(0xA01B0E04,0x5F03E802),
+ MK_64(0x33840451,0xED912885),
+ MK_64(0x374AFB04,0xEAEC2E1C),
+ MK_64(0xDF25A0E2,0x813581F7),
+ MK_64(0xE4004093,0x8B12F9D2),
+ MK_64(0xA662D539,0xC2ED39B6),
+ MK_64(0xFA8B85CF,0x45D8C75A),
+ MK_64(0x8316ED8E,0x29EDE796),
+ MK_64(0x053289C0,0x2E9F91B8),
+ MK_64(0xC3F8EF1D,0x6D518B73),
+ MK_64(0xBDCEC3C4,0xD5EF332E),
+ MK_64(0x549A7E52,0x22974487),
+ MK_64(0x67070872,0x5B749816),
+ MK_64(0xB9CD28FB,0xF0581BD1),
+ MK_64(0x0E2940B8,0x15804974)
+ };
+
+/* blkSize = 1024 bits. hashSize = 1024 bits */
+const u64b_t SKEIN1024_IV_1024[] =
+ {
+ MK_64(0xD593DA07,0x41E72355),
+ MK_64(0x15B5E511,0xAC73E00C),
+ MK_64(0x5180E5AE,0xBAF2C4F0),
+ MK_64(0x03BD41D3,0xFCBCAFAF),
+ MK_64(0x1CAEC6FD,0x1983A898),
+ MK_64(0x6E510B8B,0xCDD0589F),
+ MK_64(0x77E2BDFD,0xC6394ADA),
+ MK_64(0xC11E1DB5,0x24DCB0A3),
+ MK_64(0xD6D14AF9,0xC6329AB5),
+ MK_64(0x6A9B0BFC,0x6EB67E0D),
+ MK_64(0x9243C60D,0xCCFF1332),
+ MK_64(0x1A1F1DDE,0x743F02D4),
+ MK_64(0x0996753C,0x10ED0BB8),
+ MK_64(0x6572DD22,0xF2B4969A),
+ MK_64(0x61FD3062,0xD00A579A),
+ MK_64(0x1DE0536E,0x8682E539)
+ };
+
+#endif /* _SKEIN_IV_H_ */
diff --git a/srtp/crypto/skein_port.h b/srtp/crypto/skein_port.h new file mode 100644 index 0000000..256e9d5 --- /dev/null +++ b/srtp/crypto/skein_port.h @@ -0,0 +1,127 @@ +#ifndef _SKEIN_PORT_H_
+#define _SKEIN_PORT_H_
+/*******************************************************************
+**
+** Platform-specific definitions for Skein hash function.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+** Many thanks to Brian Gladman for his portable header files.
+**
+** To port Skein to an "unsupported" platform, change the definitions
+** in this file appropriately.
+**
+********************************************************************/
+
+#include <crypto/brg_types.h> /* get integer type definitions */
+
+/*r3gis3r : android already has that defined in types */
+#ifndef ANDROID
+typedef unsigned int uint_t; /* native unsigned integer */
+#endif
+typedef uint_8t u08b_t; /* 8-bit unsigned integer */
+typedef uint_64t u64b_t; /* 64-bit unsigned integer */
+
+#ifndef RotL_64
+#define RotL_64(x,N) (((x) << (N)) | ((x) >> (64-(N))))
+#endif
+
+/*
+ * Skein is "natively" little-endian (unlike SHA-xxx), for optimal
+ * performance on x86 CPUs. The Skein code requires the following
+ * definitions for dealing with endianness:
+ *
+ * SKEIN_NEED_SWAP: 0 for little-endian, 1 for big-endian
+ * Skein_Put64_LSB_First
+ * Skein_Get64_LSB_First
+ * Skein_Swap64
+ *
+ * If SKEIN_NEED_SWAP is defined at compile time, it is used here
+ * along with the portable versions of Put64/Get64/Swap64, which
+ * are slow in general.
+ *
+ * Otherwise, an "auto-detect" of endianness is attempted below.
+ * If the default handling doesn't work well, the user may insert
+ * platform-specific code instead (e.g., for big-endian CPUs).
+ *
+ */
+#ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */
+
+#include <crypto/brg_endian.h> /* get endianness selection */
+#if PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN
+ /* here for big-endian CPUs */
+#define SKEIN_NEED_SWAP (1)
+#elif PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN
+ /* here for x86 and x86-64 CPUs (and other detected little-endian CPUs) */
+#define SKEIN_NEED_SWAP (0)
+#if PLATFORM_MUST_ALIGN == 0 /* ok to use "fast" versions? */
+#define Skein_Put64_LSB_First(dst08,src64,bCnt) memcpy(dst08,src64,bCnt)
+#define Skein_Get64_LSB_First(dst64,src08,wCnt) memcpy(dst64,src08,8*(wCnt))
+#endif
+#else
+#error "Skein needs endianness setting!"
+#endif
+
+#endif /* ifndef SKEIN_NEED_SWAP */
+
+/*
+ ******************************************************************
+ * Provide any definitions still needed.
+ ******************************************************************
+ */
+#ifndef Skein_Swap64 /* swap for big-endian, nop for little-endian */
+#if SKEIN_NEED_SWAP
+#define Skein_Swap64(w64) \
+ ( (( ((u64b_t)(w64)) & 0xFF) << 56) | \
+ (((((u64b_t)(w64)) >> 8) & 0xFF) << 48) | \
+ (((((u64b_t)(w64)) >>16) & 0xFF) << 40) | \
+ (((((u64b_t)(w64)) >>24) & 0xFF) << 32) | \
+ (((((u64b_t)(w64)) >>32) & 0xFF) << 24) | \
+ (((((u64b_t)(w64)) >>40) & 0xFF) << 16) | \
+ (((((u64b_t)(w64)) >>48) & 0xFF) << 8) | \
+ (((((u64b_t)(w64)) >>56) & 0xFF) ) )
+#else
+#define Skein_Swap64(w64) (w64)
+#endif
+#endif /* ifndef Skein_Swap64 */
+
+
+#ifndef Skein_Put64_LSB_First
+void Skein_Put64_LSB_First(u08b_t *dst,const u64b_t *src,size_t bCnt)
+#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */
+ { /* this version is fully portable (big-endian or little-endian), but slow */
+ size_t n;
+
+ for (n=0;n<bCnt;n++)
+ dst[n] = (u08b_t) (src[n>>3] >> (8*(n&7)));
+ }
+#else
+ ; /* output only the function prototype */
+#endif
+#endif /* ifndef Skein_Put64_LSB_First */
+
+
+#ifndef Skein_Get64_LSB_First
+void Skein_Get64_LSB_First(u64b_t *dst,const u08b_t *src,size_t wCnt)
+#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */
+ { /* this version is fully portable (big-endian or little-endian), but slow */
+ size_t n;
+
+ for (n=0;n<8*wCnt;n+=8)
+ dst[n/8] = (((u64b_t) src[n ]) ) +
+ (((u64b_t) src[n+1]) << 8) +
+ (((u64b_t) src[n+2]) << 16) +
+ (((u64b_t) src[n+3]) << 24) +
+ (((u64b_t) src[n+4]) << 32) +
+ (((u64b_t) src[n+5]) << 40) +
+ (((u64b_t) src[n+6]) << 48) +
+ (((u64b_t) src[n+7]) << 56) ;
+ }
+#else
+ ; /* output only the function prototype */
+#endif
+#endif /* ifndef Skein_Get64_LSB_First */
+
+#endif /* ifndef _SKEIN_PORT_H_ */
|