summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Purcell <msp@debian.org>2013-07-09 15:55:55 +0100
committerMark Purcell <msp@debian.org>2013-07-09 15:55:55 +0100
commit669109e369a1be69ff7c4108eb545eff4c5c26d9 (patch)
tree73c117a2e7dd22a7a6ee315101f6357ab43386ec
libzrtpcpp (2.3.4-1) unstable; urgency=medium
* New upstream release - Fixes "CVE-2013-2221 CVE-2013-2222 CVE-2013-2223" (Closes: #714650) # imported from the archive
-rw-r--r--.gitignore18
-rw-r--r--AUTHORS6
-rwxr-xr-xCMakeLists.txt200
-rw-r--r--COPYING676
-rw-r--r--ChangeLog26
-rwxr-xr-xINSTALL22
-rwxr-xr-xNEWS238
-rw-r--r--README.md105
-rw-r--r--cmake/Modules/AutoArgs.cmake39
-rwxr-xr-xcmake/Modules/FindGcryptConfig.cmake241
-rw-r--r--cmake/Modules/GeneratePackage.cmake75
-rwxr-xr-xcmake/Modules/LibFindMacros.cmake99
-rwxr-xr-xcmake/Modules/SourceDistribution.cmake218
-rw-r--r--cmake/Modules/UseRPMTools.cmake177
-rw-r--r--cmake/cmake_uninstall.cmake.in20
-rwxr-xr-xdebian/backports/sarge13
-rw-r--r--debian/changelog237
-rw-r--r--debian/compat1
-rw-r--r--debian/control33
-rw-r--r--debian/copyright146
-rw-r--r--debian/docs1
-rw-r--r--debian/libzrtpcpp-dev.install3
-rw-r--r--debian/libzrtpcpp2.install1
-rwxr-xr-xdebian/rules6
-rw-r--r--debian/source/format1
-rw-r--r--debian/watch3
-rwxr-xr-xdemo/CMakeLists.txt19
-rw-r--r--demo/README27
-rw-r--r--demo/wrappertest.c169
-rw-r--r--demo/zrtptest.cpp665
-rw-r--r--demo/zrtptestMulti.cpp715
-rw-r--r--directive3
-rw-r--r--doc/Doxymini1298
-rw-r--r--doc/fdl.texi452
-rw-r--r--doc/fdlnotice.texi6
-rw-r--r--doc/gpl.texi392
-rw-r--r--libzrtpcpp-config.h.cmake53
-rw-r--r--libzrtpcpp.pc.cmake15
-rw-r--r--libzrtpcpp.spec123
-rw-r--r--libzrtpcpp.spec.cmake123
-rw-r--r--src/Base32.cpp335
-rwxr-xr-xsrc/CMakeLists.txt87
-rw-r--r--src/ZIDFile.cpp430
-rw-r--r--src/ZIDRecord.cpp113
-rw-r--r--src/Zrtp.cpp2527
-rw-r--r--src/ZrtpCWrapper.cpp470
-rw-r--r--src/ZrtpCallbackWrapper.cpp151
-rw-r--r--src/ZrtpConfigure.cpp496
-rw-r--r--src/ZrtpCrc32.cpp223
-rw-r--r--src/ZrtpPacketClearAck.cpp42
-rw-r--r--src/ZrtpPacketCommit.cpp50
-rw-r--r--src/ZrtpPacketConf2Ack.cpp42
-rw-r--r--src/ZrtpPacketConfirm.cpp87
-rw-r--r--src/ZrtpPacketDHPart.cpp98
-rw-r--r--src/ZrtpPacketError.cpp46
-rw-r--r--src/ZrtpPacketErrorAck.cpp42
-rw-r--r--src/ZrtpPacketGoClear.cpp45
-rw-r--r--src/ZrtpPacketHello.cpp129
-rw-r--r--src/ZrtpPacketHelloAck.cpp44
-rw-r--r--src/ZrtpPacketPing.cpp45
-rw-r--r--src/ZrtpPacketPingAck.cpp45
-rw-r--r--src/ZrtpPacketRelayAck.cpp42
-rw-r--r--src/ZrtpPacketSASrelay.cpp75
-rw-r--r--src/ZrtpQueue.cpp847
-rw-r--r--src/ZrtpStateClass.cpp1473
-rw-r--r--src/ZrtpTextData.cpp97
-rw-r--r--src/libzrtpcpp/Base32.h228
-rwxr-xr-xsrc/libzrtpcpp/CMakeLists.txt9
-rw-r--r--src/libzrtpcpp/TimeoutProvider.h303
-rw-r--r--src/libzrtpcpp/ZIDFile.h157
-rw-r--r--src/libzrtpcpp/ZIDRecord.h307
-rw-r--r--src/libzrtpcpp/ZRtp.h1292
-rw-r--r--src/libzrtpcpp/ZrtpCWrapper.h1339
-rw-r--r--src/libzrtpcpp/ZrtpCallback.h367
-rw-r--r--src/libzrtpcpp/ZrtpCallbackWrapper.h101
-rwxr-xr-xsrc/libzrtpcpp/ZrtpCodes.h164
-rw-r--r--src/libzrtpcpp/ZrtpConfigure.h551
-rwxr-xr-xsrc/libzrtpcpp/ZrtpCrc32.h75
-rw-r--r--src/libzrtpcpp/ZrtpPacketBase.h147
-rw-r--r--src/libzrtpcpp/ZrtpPacketClearAck.h54
-rw-r--r--src/libzrtpcpp/ZrtpPacketCommit.h134
-rw-r--r--src/libzrtpcpp/ZrtpPacketConf2Ack.h60
-rw-r--r--src/libzrtpcpp/ZrtpPacketConfirm.h125
-rw-r--r--src/libzrtpcpp/ZrtpPacketDHPart.h120
-rw-r--r--src/libzrtpcpp/ZrtpPacketError.h68
-rw-r--r--src/libzrtpcpp/ZrtpPacketErrorAck.h56
-rw-r--r--src/libzrtpcpp/ZrtpPacketGoClear.h72
-rw-r--r--src/libzrtpcpp/ZrtpPacketHello.h191
-rw-r--r--src/libzrtpcpp/ZrtpPacketHelloAck.h59
-rw-r--r--src/libzrtpcpp/ZrtpPacketPing.h67
-rw-r--r--src/libzrtpcpp/ZrtpPacketPingAck.h74
-rw-r--r--src/libzrtpcpp/ZrtpPacketRelayAck.h56
-rw-r--r--src/libzrtpcpp/ZrtpPacketSASrelay.h113
-rw-r--r--src/libzrtpcpp/ZrtpQueue.h917
-rw-r--r--src/libzrtpcpp/ZrtpStateClass.h324
-rw-r--r--src/libzrtpcpp/ZrtpStates.h90
-rw-r--r--src/libzrtpcpp/ZrtpTextData.h124
-rw-r--r--src/libzrtpcpp/ZrtpUserCallback.h234
-rwxr-xr-xsrc/libzrtpcpp/crypto/TwoCFB.cpp79
-rw-r--r--src/libzrtpcpp/crypto/ZrtpDH.h168
-rw-r--r--src/libzrtpcpp/crypto/aesCFB.h87
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/InitializeGcrypt.cpp130
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcryptAesCFB.cpp77
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcryptZrtpDH.cpp349
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcrypthmac256.cpp68
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcrypthmac384.cpp68
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcryptsha256.cpp89
-rw-r--r--src/libzrtpcpp/crypto/gcrypt/gcryptsha384.cpp89
-rw-r--r--src/libzrtpcpp/crypto/hmac256.h96
-rw-r--r--src/libzrtpcpp/crypto/hmac384.h96
-rw-r--r--src/libzrtpcpp/crypto/openssl/AesCFB.cpp89
-rwxr-xr-xsrc/libzrtpcpp/crypto/openssl/InitializeOpenSSL.cpp242
-rw-r--r--src/libzrtpcpp/crypto/openssl/ZrtpDH.cpp426
-rw-r--r--src/libzrtpcpp/crypto/openssl/hmac256.cpp67
-rw-r--r--src/libzrtpcpp/crypto/openssl/hmac384.cpp67
-rw-r--r--src/libzrtpcpp/crypto/openssl/sha256.cpp97
-rw-r--r--src/libzrtpcpp/crypto/openssl/sha384.cpp97
-rw-r--r--src/libzrtpcpp/crypto/sha256.h144
-rw-r--r--src/libzrtpcpp/crypto/sha384.h144
-rwxr-xr-xsrc/libzrtpcpp/crypto/twoCFB.h87
-rw-r--r--src/libzrtpcpp/crypto/twofish.c1733
-rwxr-xr-xsrc/libzrtpcpp/crypto/twofish.h265
-rwxr-xr-xsrc/libzrtpcpp/crypto/twofish_cfb.c82
-rw-r--r--src/libzrtpcpp/zrtpPacket.h342
-rw-r--r--src/libzrtpcpp/zrtpccrtp.h76
-rw-r--r--srtp/CryptoContext.cpp465
-rw-r--r--srtp/CryptoContext.h418
-rw-r--r--srtp/CryptoContextCtrl.cpp408
-rw-r--r--srtp/CryptoContextCtrl.h327
-rw-r--r--srtp/crypto/SrtpSymCrypto.h393
-rw-r--r--srtp/crypto/brg_endian.h148
-rw-r--r--srtp/crypto/brg_types.h188
-rw-r--r--srtp/crypto/gcrypt/InitializeGcrypt.cpp84
-rw-r--r--srtp/crypto/gcrypt/gcryptSrtpSymCrypto.cpp344
-rw-r--r--srtp/crypto/gcrypt/gcrypthmac.cpp117
-rw-r--r--srtp/crypto/hmac.h172
-rw-r--r--srtp/crypto/macSkein.cpp89
-rw-r--r--srtp/crypto/macSkein.h148
-rw-r--r--srtp/crypto/openssl/.kdev_include_paths1
-rw-r--r--srtp/crypto/openssl/SrtpSymCrypto.cpp326
-rw-r--r--srtp/crypto/openssl/hmac.cpp106
-rw-r--r--srtp/crypto/skein.c742
-rw-r--r--srtp/crypto/skein.h327
-rw-r--r--srtp/crypto/skeinApi.c225
-rw-r--r--srtp/crypto/skeinApi.h253
-rw-r--r--srtp/crypto/skein_block.c689
-rw-r--r--srtp/crypto/skein_iv.h199
-rw-r--r--srtp/crypto/skein_port.h127
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
+*~
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..94ee6a8
--- /dev/null
+++ b/AUTHORS
@@ -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)
+
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4432540
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/INSTALL b/INSTALL
new file mode 100755
index 0000000..5c1e524
--- /dev/null
+++ b/INSTALL
@@ -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.
+
+
diff --git a/NEWS b/NEWS
new file mode 100755
index 0000000..6224923
--- /dev/null
+++ b/NEWS
@@ -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
+ * f