diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..dd4b88586
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,417 @@
+#
+# SRT - Secure, Reliable, Transport
+# Copyright (c) 2017 Haivision Systems Inc.
+#
+# 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, see
+#
+cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR)
+
+if ( CYGWIN AND NOT CYGWIN_USE_POSIX )
+ set(WIN32 1)
+ set(CMAKE_LEGACY_CYGWIN_WIN32 1)
+ add_definitions(-DWIN32=1 -DCYGWIN=1)
+ message("HAVE CYGWIN. Setting backward compat CMAKE_LEGACY_CYGWIN_WIN32 and -DWIN32")
+endif()
+
+
+set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/scripts")
+include(haiUtil)
+include(FindPkgConfig)
+
+
+set (SRT_VERSION "1.2.0")
+
+set (NAME_haisrt haisrt)
+set (NAME_haicrypt haicrypt)
+
+if ( DEFINED WITH_SRT_NAME )
+ set (NAME_haisrt ${WITH_SRT_NAME})
+endif()
+
+if ( DEFINED WITH_HAICRYPT_NAME )
+ set (NAME_haicrypt ${WITH_HAICRYPT_NAME})
+endif()
+
+set_if(DARWIN ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+set_if(LINUX ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+
+# Prepare openssl variables:
+# WITH_OPENSSL: general
+# WITH_OPENSSL_INCLUDEDIR: /include
+# WITH_OPENSSL_LIBDIR: /lib
+# WITH_OPENSSL_LIBRARIES: empty as default, in which case it uses -lcrypto -lpthread
+
+set (HAVE_OPENSSL 0)
+if (DEFINED WITH_OPENSSL_INCLUDEDIR AND (DEFINED WITH_OPENSSL_LIBDIR OR DEFINED WITH_OPENSSL_LDFLAGS))
+ set (HAVE_OPENSSL 1)
+else()
+
+ if(DEFINED WITH_OPENSSL)
+ set (WITH_OPENSSL_INCLUDEDIR ${WITH_OPENSSL}/include)
+ set (WITH_OPENSSL_LIBDIR ${WITH_OPENSSL}/lib)
+ set (HAVE_OPENSSL 1)
+ endif()
+endif()
+
+
+if ( WITH_COMPILER_PREFIX )
+
+ set(CMAKE_CXX_COMPILER ${WITH_COMPILER_PREFIX}g++)
+ set(CMAKE_C_COMPILER ${WITH_COMPILER_PREFIX}gcc)
+
+endif()
+
+if ( CMAKE_CROSSCOMPILING )
+ set (HAVE_CROSSCOMPILER 1)
+endif()
+
+
+set (cid ${CMAKE_CXX_COMPILER_ID})
+set (gnu_compat_compilers Intel Clang GNU)
+
+# Perfect function, however it somehow doesn't work on Cygwin with cmake 3.6.0 :D
+#list(FIND "${gnu_compat_compilers}" ${CMAKE_CXX_COMPILER_ID} HAVE_COMPILER_GNU_COMPAT)
+
+set(HAVE_COMPILER_GNU_COMPAT 0)
+foreach (gnid Intel Clang GNU)
+ if (${CMAKE_CXX_COMPILER_ID} STREQUAL ${gnid})
+ set(HAVE_COMPILER_GNU_COMPAT 1)
+ break()
+ endif()
+endforeach()
+
+#message( "In ${gnu_compat_compilers} found ${CMAKE_CXX_COMPILER_ID} at position ${i}")
+if (${HAVE_COMPILER_GNU_COMPAT})
+ message("COMPILER: GNU compat: ${CMAKE_CXX_COMPILER}")
+else()
+ message("COMPILER: NOT GNU compat: ${CMAKE_CXX_COMPILER}")
+endif()
+
+
+if (NOT HAVE_OPENSSL)
+ message(FATAL_ERROR "OpenSSL location not provided. Please define at least WITH_OPENSSL for prefix path.")
+endif()
+
+# Openssl should be added as a dependency for haisrt
+if (NOT DEFINED WITH_OPENSSL_LIBRARIES)
+ if (NOT DEFINED WITH_OPENSSL_LDFLAGS)
+ message(FATAL_ERROR "Need defined one of WITH_OPENSSL_LIBRARIES (filenames), WITH_OPENSSL_LDFLAGS (-l flags)")
+ endif()
+ separate_arguments(WITH_OPENSSL_LDFLAGS)
+
+else()
+ # If so, then add each file from this list with the directory specified in _LIBDIR to the list
+ separate_arguments(WITH_OPENSSL_LIBRARIES)
+ adddirname(${WITH_OPENSSL_LIBDIR} "${WITH_OPENSSL_LIBRARIES}" WITH_OPENSSL_LDFLAGS)
+ unset (WITH_OPENSSL_LIBDIR)
+endif()
+
+if (NOT ${WITH_OPENSSL} STREQUAL "/usr")
+ include_directories(${WITH_OPENSSL_INCLUDEDIR})
+endif()
+
+# Pthread - optional
+# The following things must be specified on particular platform:
+# - the -lpthread (or -pthread?) flag on Linux (as link-libraries)
+# - the path to pthread API library on Windows (as link-libraries)
+
+if (DEFINED WITH_PTHREAD_INCLUDEDIR)
+ include_directories(${WITH_PTHREAD_INCLUDEDIR})
+endif()
+
+
+if ( DISABLE_CXX11 )
+ set (ENABLE_CXX11 0)
+elseif( DEFINED ENABLE_CXX11 )
+else()
+ set (ENABLE_CXX11 1)
+endif()
+
+if ( ENABLE_CXX11 EQUAL 0 )
+ message("NOTE: Parts that require C++11 support will be disabled (stransmit)")
+endif()
+
+
+# Define Haisrt version
+set_version_variables(SRT_VERSION ${SRT_VERSION})
+message( "VERSION: major=${SRT_VERSION_MAJOR} minor=${SRT_VERSION_MINOR} patch=${SRT_VERSION_PATCH}" )
+
+set (PACKAGE haisrt)
+set (PACKAGE_BUGREPORT to-be-set)
+
+if (ENABLE_DEBUG)
+ set (CMAKE_BUILD_TYPE "Debug")
+endif()
+
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+ set (SRT_DEBUG_ENABLED 1)
+ set (SRT_DEBUG_OPT "-ggdb -O0 -D_DEBUG")
+ message("Using DEBUG mode")
+else()
+ set (SRT_DEBUG_ENABLED 0)
+ set (SRT_DEBUG_OPT "-O2")
+ message("Using RELEASE mode")
+endif()
+
+# Enuff.
+
+if ( ENABLE_PROFILE OR "$ENV{HAI_BUILD_PROFILE}" STREQUAL "1" )
+ message("+++ Build will include PROFILE INFORMATION")
+ set (SRT_BUILD_PROFILE 1)
+else()
+ set (SRT_BUILD_PROFILE 0)
+endif()
+
+if (HAVE_COMPILER_GNU_COMPAT)
+ set (SRT_GCC_WARN "-Wall -Wextra")
+else()
+ # cpp debugging on Windows :D
+ #set (SRT_GCC_WARN "/showIncludes")
+endif()
+
+# Logging is always enabled when debug build, and can be
+# additionally enabled by setting SRT_LOGGING_ENABLED env to 1.
+if ( "$ENV{SRT_LOGGING_ENABLED}" STREQUAL "1" )
+ set(ENABLE_LOGGING 1)
+endif()
+
+if ( ENABLE_LOGGING OR SRT_DEBUG_ENABLED )
+ list(APPEND SRT_EXTRA_FLAGS "-DENABLE_LOGGING=1")
+endif()
+
+if ( ENABLE_DYNAMIC )
+ set (srt_libspec SHARED)
+ set (srt_install LIBRARY)
+else()
+ set (srt_libspec STATIC)
+ set (srt_install ARCHIVE)
+endif()
+
+
+set (SRT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include)
+
+set (SRT_SRC_HAICRYPT_DIR ${CMAKE_SOURCE_DIR}/haicrypt)
+set (SRT_SRC_SRTCORE_DIR ${CMAKE_SOURCE_DIR}/srtcore)
+set (SRT_SRC_COMMON_DIR ${CMAKE_SOURCE_DIR}/common)
+set (SRT_SRC_TOOLS_DIR ${CMAKE_SOURCE_DIR}/tools)
+
+# This means: set LIBSRTCORE_SRCFILES [pfind *.c *.cpp $SRT_SRC_SRTCORE_DIR]
+aux_source_directory(${SRT_SRC_SRTCORE_DIR} LIBSRTCORE_SRCFILES)
+if(WIN32)
+ message( "WIN32 detected - including win_time.cpp" )
+
+ # Add Windows specific source files here.
+ # These should provide various POSIX/Linux features
+ # to Windows.
+ set(winspec_SOURCES ${SRT_SRC_SRTCORE_DIR}/windows/win_time.cpp)
+
+ if (NOT DEFINED WITH_PTHREAD_LDFLAGS OR NOT DEFINED WITH_PTHREAD_INCLUDEDIR)
+ message(FATAL_ERROR "On Windows, PTHREAD library is 3rd party. WITH_PTHREAD_* variables must be specified.")
+ endif()
+elseif(DARWIN)
+ message( "DARWIN detected")
+ add_definitions(-DOSX=1)
+elseif(LINUX)
+ add_definitions(-DLINUX=1)
+ message( "LINUX detected" )
+else()
+ message(FATAL_ERROR "Unsupported system")
+endif()
+aux_source_directory(${SRT_SRC_HAICRYPT_DIR} LIBHAICRYPT_SRCFILES)
+
+
+set (SOURCES_haisrtbase
+ ${winspec_SOURCES} # Adds ONLY ON WINDOWS its gettimeofday (used in haicrypt and srtcore)
+ ${SOURCES_haicrypt_dep}
+)
+
+set (SOURCES_haisrt
+ ${LIBSRTCORE_SRCFILES}
+ ${SRT_SRC_COMMON_DIR}/srt_compat.c
+)
+
+set (SOURCES_haicrypt
+ ${LIBHAICRYPT_SRCFILES}
+)
+
+include_directories(
+ ${SRT_SRC_SRTCORE_DIR}
+ ${SRT_SRC_HAICRYPT_DIR}
+ ${SRT_INCLUDE_DIR}
+)
+
+add_definitions(
+ -D_GNU_SOURCE
+ -DHAI_PATCH=1
+ -DHAI_ENABLE_SRT=1
+ -DHAICRYPT_USE_OPENSSL_EVP=1
+ -DHAICRYPT_USE_OPENSSL_AES
+ -DSRT_VERSION="${SRT_VERSION}"
+)
+
+if ( ${SRT_BUILD_PROFILE} )
+ # They are actually cflags, not definitions, but CMake is stupid enough.
+ add_definitions(-g -pg)
+endif()
+
+
+
+set (HEADERS_haisrt
+ ${SRT_SRC_SRTCORE_DIR}/srt.h
+ ${SRT_SRC_SRTCORE_DIR}/udt.h
+ ${SRT_SRC_SRTCORE_DIR}/srt4udt.h
+ ${SRT_SRC_SRTCORE_DIR}/logging_api.h
+
+)
+
+set (HEADERS_haicrypt
+ ${SRT_INCLUDE_DIR}/haicrypt.h
+ ${SRT_INCLUDE_DIR}/hcrypt_ctx.h
+ ${SRT_INCLUDE_DIR}/hcrypt_msg.h
+)
+
+set (ALL_LIBS "")
+
+#message("LIBRARY SPECIFICATION: ${srt_libspec}")
+
+# HaiCrypt: cryptography wrapper, used by HaiSRT.
+
+#message("HaiCrypt: SRC: ${SOURCES_haicrypt} ${SOURCES_haisrtbase}")
+
+add_library(${NAME_haicrypt} ${srt_libspec} ${SOURCES_haicrypt} ${SOURCES_haisrtbase})
+if (DEFINED WITH_OPENSSL_LIBDIR)
+ set_target_properties(${NAME_haicrypt} PROPERTIES LINK_DIRECTORIES ${WITH_OPENSSL_LIBDIR})
+endif()
+if (DEFINED WITH_OPENSSL_INCLUDEDIR)
+ target_include_directories(${NAME_haicrypt} PRIVATE ${WITH_OPENSSL_INCLUDEDIR})
+endif()
+set (LDFLAGS_haicrypt_STATIC ${WITH_OPENSSL_LDFLAGS})
+target_link_libraries(${NAME_haicrypt} ${LDFLAGS_haicrypt_STATIC})
+get_target_property(LDFLAGS_haicrypt_RESULT ${NAME_haicrypt} LINK_LIBRARIES)
+#message("HaiCrypt: LDFLAGS: ${LDFLAGS_haicrypt_RESULT}")
+
+install(TARGETS ${NAME_haicrypt} ${srt_install} DESTINATION lib)
+install(FILES ${HEADERS_haicrypt} DESTINATION include/haicrypt)
+list(APPEND ALL_LIBS ${NAME_haicrypt})
+
+# HaiSRT: SRT library (UDT API only)
+#message("HaiSRT: SRC: ${SOURCES_haisrt}")
+add_library(${NAME_haisrt} ${srt_libspec} ${SOURCES_haisrt})
+set (LDFLAGS_haisrt ${WITH_PTHREAD_LDFLAGS})
+
+# Not sure why it's required, but somehow only on Linux
+if ( LINUX )
+ list(APPEND LDFLAGS_haisrt -lrt)
+endif()
+list(APPEND LDFLAGS_srt_STATIC ${LDFLAGS_haisrt} ${LDFLAGS_haicrypt_STATIC})
+target_link_libraries(${NAME_haisrt} ${NAME_haicrypt} ${LDFLAGS_srt_STATIC})
+
+get_target_property(LDFLAGS_srt_RESULT ${NAME_haisrt} LINK_LIBRARIES)
+#message("HaiSRT: LDFLAGS: ${LDFLAGS_srt_RESULT}")
+
+install(TARGETS ${NAME_haisrt} ${srt_install} DESTINATION lib)
+install(FILES ${HEADERS_haisrt} DESTINATION include/srt)
+list(APPEND ALL_LIBS ${NAME_haisrt})
+
+if ( ${SRT_BUILD_PROFILE} )
+ target_link_libraries(${NAME_haisrt} -g -pg)
+endif()
+
+if ( WIN32 )
+ target_compile_definitions(${NAME_haisrt} PRIVATE -DUDT_EXPORTS)
+ link_libraries(Ws2_32.lib)
+ add_definitions(-DPTW32_STATIC_LIB)
+endif()
+
+
+set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SRT_DEBUG_OPT} ${SRT_EXTRA_FLAGS} ${SRT_GCC_WARN}")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SRT_DEBUG_OPT} ${SRT_EXTRA_FLAGS} ${SRT_GCC_WARN}")
+
+
+# PC file generation.
+if (NOT DEFINED INSTALLDIR)
+ set (INSTALLDIR ${CMAKE_INSTALL_PREFIX})
+ get_filename_component(INSTALLDIR ${INSTALLDIR} ABSOLUTE)
+endif()
+
+# SRT_VERSION is there
+if ( ${srt_libspec} STREQUAL "SHARED" )
+ if ( LINUX )
+ set (IFNEEDED_SRT_LDFLAGS -pthread)
+ endif()
+ set (LIBS_srtapps ${NAME_haisrt} ${NAME_haicrypt})
+else()
+ # Well, cmake has a basic functionality problem.
+ # Even string(REPLACE) cannot do this - it replaces it with empty string!!!
+ #string(REPLACE ";" " " IFNEEDED_SRT_LDFLAGS ${IFNEEDED_SRT_LDFLAGS})
+ set (IFNEEDED_SRT_LDFLAGS "")
+ # If there's separate_arguments, maybe there's also join_arguments?
+ foreach (OPT ${LDFLAGS_srt_STATIC})
+ set (IFNEEDED_SRT_LDFLAGS "${IFNEEDED_SRT_LDFLAGS} ${OPT}")
+ endforeach()
+ set (LIBS_srtapps ${NAME_haisrt} ${NAME_haicrypt} ${LDFLAGS_srt_STATIC} ${LDFLAGS_haicrypt_STATIC})
+endif()
+
+# XXX
+# These two flags are required if compiling a C application
+# The problem is that pkg-config cannot return flags that are
+# suitable for C or C++ only - just "cflags", and for C by default.
+# This may cause trouble when you want to compile your app with static libstdc++;
+# if your build requires it, you'd probably remove -lstdc++ from the list
+# obtained by `pkg-config --libs`.
+#
+# Some sensible solution for that is desired.
+if ( LINUX )
+ set (IFNEEDED_SRT_LDFLAGS "${IFNEEDED_SRT_LDFLAGS} -lstdc++ -lm")
+endif()
+
+configure_file(scripts/haisrt.pc.in haisrt.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/haisrt.pc DESTINATION lib/pkgconfig)
+
+# Applications
+
+if ( HAVE_COMPILER_GNU_COMPAT )
+ message("C++ VERSION: Setting C++11 compat flag for gnu compiler")
+ set (CFLAGS_CXX_STANDARD "-std=c++11")
+else()
+ message("C++ VERSION: leaving default, not a GNU compiler, assuming C++11 or newer is default.")
+ set (CFLAGS_CXX_STANDARD "")
+endif()
+
+if ( ENABLE_CXX11 )
+
+ add_executable(stransmit ${CMAKE_SOURCE_DIR}/apps/stransmit.cpp ${CMAKE_SOURCE_DIR}/apps/uriparser.cpp)
+
+ # Test programs
+ add_executable(utility-test ${CMAKE_SOURCE_DIR}/apps/utility-test.cpp)
+
+ # We state that Darwin always uses CLANG compiler, which honors this flag the same way.
+ set_target_properties(stransmit PROPERTIES COMPILE_FLAGS "${CFLAGS_CXX_STANDARD}")
+ target_link_libraries(stransmit ${LIBS_srtapps})
+ install(TARGETS stransmit RUNTIME DESTINATION bin)
+ install(PROGRAMS scripts/sfplay DESTINATION bin)
+
+endif()
+
+if ( ENABLE_SUFLIP )
+ set (SOURCES_suflip
+ ${CMAKE_SOURCE_DIR}/apps/suflip.cpp ${CMAKE_SOURCE_DIR}/apps/uriparser.cpp
+ )
+
+ set(LIBS_suflip ${NAME_haicrypt} ${NAME_haisrt})
+
+ add_executable(suflip ${SOURCES_suflip})
+ target_link_libraries(suflip ${LIBS_suflip})
+ install(TARGETS suflip RUNTIME DESTINATION bin)
+endif ()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..e4862777d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+## License
+
+By contributing code to the [SRT project](https://github.com/Haivision/srt/), you agree to license your contribution under the [LGPLv2.1 License](COPYING).
+
+## Issues
+
+Open a GitHub issue for anything you find or any questions you have.
+
+## Comments
+
+Comment on any GitHub issue, open or closed. The only guidelines here are to be friendly and welcoming. If you see that a question has been asked and you think you know the answer, don't wait!
+
+## Pull Requests
+
+Submit a pull request at any time, whether an issue has been created or not. It may be helpful to discuss your goals in an issue first, though many things can best be shown with code.
+
+## Code Style
+
+Please follow existing style.
+
+## Attribution
+
+This contributing guide is adapted from [VVV's guide](https://github.com/Varying-Vagrant-Vagrants/VVV/blob/develop/.github/CONTRIBUTING.md).
+
+## Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+* (a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+* (b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+* (c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+* (d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
\ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000..4362b4915
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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.
+
+
+ Copyright (C)
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..7c4c0e75b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,86 @@
+REQUIREMENTS:
+============
+
+* cmake (as build system)
+* OpenSSL
+* Pthreads (for POSIX systems it's builtin, for Windows there's a library)
+
+For Linux:
+==========
+
+Install cmake and openssl-devel (or similar name) package. For pthreads
+there should be -lpthreads linker flag added.
+
+## Ubunut 14
+```
+sudo apt-get update
+sudo apt-get upgrade
+sudo apt-get install tclsh pkg-config cmake libssl-dev build-essential
+./configure
+make
+```
+## CentOS 7
+```
+sudo yum update
+sudo yum install tcl pkgconfig openssl-devel cmake gcc gcc-c++ make automake
+./configure
+make
+```
+
+For Mac (Darwin, iOS):
+=====================
+
+Install cmake and openssl with development files from "brew". Note that the
+system version of OpenSSL is inappropriate, although you should be able to
+use any newer version compiled from sources, if you prefer.
+
+For Windows:
+============
+
+1. Install cmake for Windows. The CMake GUI will help you configure the project.
+Note that some variables must be provided explicitly. These are the default
+recommended values (required until some solution for running the `configure`
+script in Windows can be found):
+
+ WITH_OPENSSL_INCLUDEDIR=C:/OpenSSL-Win64/include
+ WITH_OPENSSL_LIBDIR=C:/OpenSSL-win64/lib/VC/static
+ WITH_OPENSSL_LIBRARIES=libeay32MT.lib ssleay32MT.lib
+ WITH_PTHREAD_INCLUDEDIR=C:/pthread-win32/include
+ WITH_PTHREAD_LDFLAGS=C:/pthread-win32/lib/pthread_lib.lib
+
+
+2. Please download and install OpenSSL for Windows.
+
+The 64-bit devel package can be downloaded from here:
+
+ http://slproweb.com/download/Win64OpenSSL-1_0_2a.exe
+
+It's expected to be installed in `C:\OpenSSL-Win64` (see the above variables).
+
+
+3. Compile and install Pthreads for Windows from this submodule:
+
+ submodules/pthread-win32
+
+Please follow the steps:
+
+a. Using Visual Studio 2013, please open this file:
+
+ pthread_lib.2013.vcxproj
+
+b. Make sure to select configuration: `Release` and `x64`.
+
+c. Make sure that the `pthread_lib` project will be built.
+
+d. After building, find the `pthread_lib.lib` file (directory is probably: `bin\x64_MSVC2013.Release`).
+Copy this file to `C:\pthread-win32\lib` (or whatever other location you configured in variables).
+
+e. Copy include files to `C:\pthread-win32\include` - the following ones:
+
+ pthread.h
+ sched.h
+ semaphore.h
+
+(They are in the toplevel directory, there are actually no meaningful subdirs here)
+(NOTE: the win32 is part of the project name. It will become 32 or 64 depending on selection)
+
diff --git a/apps/UDT b/apps/UDT
new file mode 120000
index 000000000..f33ded826
--- /dev/null
+++ b/apps/UDT
@@ -0,0 +1 @@
+../udt4/app
\ No newline at end of file
diff --git a/apps/appcommon.hpp b/apps/appcommon.hpp
new file mode 100644
index 000000000..94945f5c4
--- /dev/null
+++ b/apps/appcommon.hpp
@@ -0,0 +1,124 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+#if WIN32
+
+// Keep this below commented out.
+// This is for a case when you need cpp debugging on Windows.
+//#ifdef _WINSOCKAPI_
+//#error "You include somewhere, remove it. It causes conflicts"
+//#endif
+
+#include
+#include
+#include
+// WIN32 API does not have sleep() and usleep(), Although MINGW does.
+#ifdef __MINGW32__
+#include
+#else
+extern "C" inline int sleep(int seconds) { Sleep(seconds * 1000); return 0; }
+#endif
+#else
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include
+
+// NOTE: MINGW currently does not include support for inet_pton(). See
+// http://mingw.5.n7.nabble.com/Win32API-request-for-new-functions-td22029.html
+// Even if it did support inet_pton(), it is only available on Windows Vista
+// and later. Since we need to support WindowsXP and later in ORTHRUS. Many
+// customers still use it, we will need to implement using something like
+// WSAStringToAddress() which is available on Windows95 and later.
+// Support for IPv6 was added on WindowsXP SP1.
+// Header: winsock2.h
+// Implementation: ws2_32.dll
+// See:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742214(v=vs.85).aspx
+// http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html
+#ifdef __MINGW32__
+static inline int inet_pton(int af, const char * src, void * dst)
+{
+ struct sockaddr_storage ss;
+ int ssSize = sizeof(ss);
+ char srcCopy[INET6_ADDRSTRLEN + 1];
+
+ ZeroMemory(&ss, sizeof(ss));
+
+ // work around stupid non-const API
+ strncpy(srcCopy, src, INET6_ADDRSTRLEN + 1);
+ srcCopy[INET6_ADDRSTRLEN] = '\0';
+
+ if (WSAStringToAddress(
+ srcCopy, af, NULL, (struct sockaddr *)&ss, &ssSize) != 0)
+ {
+ return 0;
+ }
+
+ switch (af)
+ {
+ case AF_INET :
+ {
+ *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
+ return 1;
+ }
+ case AF_INET6 :
+ {
+ *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
+ return 1;
+ }
+ default :
+ {
+ // No-Op
+ }
+ }
+
+ return 0;
+}
+#endif // __MINGW__
+
+inline sockaddr_in CreateAddrInet(const std::string& name, unsigned short port)
+{
+ sockaddr_in sa;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(port);
+
+ if ( name != "" )
+ {
+ if ( inet_pton(AF_INET, name.c_str(), &sa.sin_addr) == 1 )
+ return sa;
+
+ // XXX RACY!!! Use getaddrinfo() instead. Check portability.
+ // Windows/Linux declare it.
+ // See:
+ // http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3b.html
+ hostent* he = gethostbyname(name.c_str());
+ if ( !he || he->h_addrtype != AF_INET )
+ throw std::invalid_argument("SrtSource: host not found: " + name);
+
+ sa.sin_addr = *(in_addr*)he->h_addr_list[0];
+ }
+
+ return sa;
+}
diff --git a/apps/stransmit.cpp b/apps/stransmit.cpp
new file mode 100644
index 000000000..4f523193f
--- /dev/null
+++ b/apps/stransmit.cpp
@@ -0,0 +1,1624 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+// NOTE: This application uses C++11.
+
+// This program uses quite a simple architecture, which is mainly related to
+// the way how it's invoked: stransmit (plus options).
+//
+// The media for and are filled by abstract classes
+// named Source and Target respectively. Most important virtuals to
+// be filled by the derived classes are Source::Read and Target::Write.
+//
+// For SRT please take a look at the SrtCommon class first. This contains
+// everything that is needed for creating an SRT medium, that is, making
+// a connection as listener, as caller, and as rendezvous. The listener
+// and caller modes are built upon the same philosophy as those for
+// BSD/POSIX socket API (bind/listen/accept or connect).
+//
+// The instance class is selected per details in the URI (usually scheme)
+// and then this URI is used to configure the medium object. Medium-specific
+// options are specified in the URI: SCHEME://HOST:PORT?opt1=val1&opt2=val2 etc.
+//
+// Options for connection are set by ConfigurePre and ConfigurePost.
+// This is a philosophy that exists also in BSD/POSIX sockets, just not
+// officially mentioned:
+// - The "PRE" options must be set prior to connecting and can't be altered
+// on a connected socket, however if set on a listening socket, they are
+// derived by accept-ed socket.
+// - The "POST" options can be altered any time on a connected socket.
+// They MAY have also some meaning when set prior to connecting; such
+// option is SRTO_RCVSYN, which makes connect/accept call asynchronous.
+// Because of that this option is treated special way in this app.
+//
+// See 'srt_options' global variable for a list of all options.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "appcommon.hpp" // CreateAddrInet
+#include "uriparser.hpp" // UriParser
+
+// NOTE: This is without "haisrt/" because it uses an internal path
+// to the library. Application using the "installed" library should
+// use
+#include
+#include
+
+// FEATURES when undefined or == 2, sets developer mode.
+// When FEATURES == 1, it enforces user mode.
+// In user mode SRT output is disabled.
+#if defined(FEATURES) && FEATURES != 0
+ #if FEATURES == 2
+ #define DEVELOPER_MODE 1
+ #warning USING DEVELOPER MODE
+ #else
+ #define DEVELOPER_MODE 0
+ #warning USING USER MODE
+ #endif
+#else
+#define DEVELOPER_MODE 1
+#endif
+
+// The length of the SRT payload used in srt_recvmsg call.
+// So far, this function must be used and up to this length of payload.
+const size_t DEFAULT_CHUNK = 1316;
+
+using namespace std;
+
+typedef std::vector bytevector;
+
+// This is based on codes taken from
+// This is POSIX standard, so it's not going to change.
+// Haivision standard only adds one more severity below
+// DEBUG named DEBUG_TRACE to satisfy all possible needs.
+
+map srt_level_names
+{
+ { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "debug", LOG_DEBUG },
+ { "emerg", LOG_EMERG },
+ { "err", LOG_ERR },
+ { "error", LOG_ERR }, /* DEPRECATED */
+ { "fatal", LOG_CRIT }, // XXX Added for SRT
+ { "info", LOG_INFO },
+ // { "none", INTERNAL_NOPRI }, /* INTERNAL */
+ { "notice", LOG_NOTICE },
+ { "note", LOG_NOTICE }, // XXX Added for SRT
+ { "panic", LOG_EMERG }, /* DEPRECATED */
+ { "warn", LOG_WARNING }, /* DEPRECATED */
+ { "warning", LOG_WARNING },
+ //{ "", -1 }
+};
+
+
+
+void PrintSrtStats(int sid, const CPerfMon& mon)
+{
+ cout << "======= SRT STATS: sid=" << sid << endl;
+ cout << "PACKETS SENT: " << mon.pktSent << " RECEIVED: " << mon.pktRecv << endl;
+ cout << "LOST PKT SENT: " << mon.pktSndLoss << " RECEIVED: " << mon.pktRcvLoss << endl;
+ cout << "REXMIT SENT: " << mon.pktRetrans << " RECEIVED: " << mon.pktRcvRetrans << endl;
+ cout << "RATE SENDING: " << mon.mbpsSendRate << " RECEIVING: " << mon.mbpsRecvRate << endl;
+ cout << "BELATED RECEIVED: " << mon.pktRcvBelated << " AVG TIME: " << mon.pktRcvAvgBelatedTime << endl;
+ cout << "REORDER DISTANCE: " << mon.pktReorderDistance << endl;
+ cout << "WINDOW: FLOW: " << mon.pktFlowWindow << " CONGESTION: " << mon.pktCongestionWindow << " FLIGHT: " << mon.pktFlightSize << endl;
+ cout << "RTT: " << mon.msRTT << "ms BANDWIDTH: " << mon.mbpsBandwidth << "Mb/s\n";
+ cout << "BUFFERLEFT: SND: " << mon.byteAvailSndBuf << " RCV: " << mon.byteAvailRcvBuf << endl;
+}
+
+logging::LogLevel::type ParseLogLevel(string level)
+{
+ using namespace logging;
+
+ if ( level.empty() )
+ return LogLevel::fatal;
+
+ if ( isdigit(level[0]) )
+ {
+ long lev = strtol(level.c_str(), 0, 10);
+ if ( lev >= SRT_LOG_LEVEL_MIN && lev <= SRT_LOG_LEVEL_MAX )
+ return LogLevel::type(lev);
+
+ cerr << "ERROR: Invalid loglevel number: " << level << " - fallback to FATAL\n";
+ return LogLevel::fatal;
+ }
+
+ int (*ToLower)(int) = &std::tolower; // manual overload resolution
+ transform(level.begin(), level.end(), level.begin(), ToLower);
+
+ auto i = srt_level_names.find(level);
+ if ( i == srt_level_names.end() )
+ {
+ cerr << "ERROR: Invalid loglevel spec: " << level << " - fallback to FATAL\n";
+ return LogLevel::fatal;
+ }
+
+ return LogLevel::type(i->second);
+}
+
+set ParseLogFA(string fa)
+{
+ using namespace logging;
+
+ set fas;
+
+ // The split algo won't work on empty string.
+ if ( fa == "" )
+ return fas;
+
+ static string names [] = { "general", "bstats", "control", "data", "tsbpd", "rexmit" };
+ size_t names_s = sizeof (names)/sizeof (names[0]);
+
+ if ( fa == "all" )
+ {
+ // Skip "general", it's always on
+ fas.insert(SRT_LOGFA_BSTATS);
+ fas.insert(SRT_LOGFA_CONTROL);
+ fas.insert(SRT_LOGFA_DATA);
+ fas.insert(SRT_LOGFA_TSBPD);
+ fas.insert(SRT_LOGFA_REXMIT);
+ return fas;
+ }
+
+ int (*ToLower)(int) = &std::tolower;
+ transform(fa.begin(), fa.end(), fa.begin(), ToLower);
+
+ vector xfas;
+ size_t pos = 0, ppos = 0;
+ for (;;)
+ {
+ if ( fa[pos] != ',' )
+ {
+ ++pos;
+ if ( pos < fa.size() )
+ continue;
+ }
+ size_t n = pos - ppos;
+ if ( n != 0 )
+ xfas.push_back(fa.substr(ppos, n));
+ ++pos;
+ if ( pos >= fa.size() )
+ break;
+ ppos = pos;
+ }
+
+ for (size_t i = 0; i < xfas.size(); ++i)
+ {
+ fa = xfas[i];
+ string* names_p = find(names, names + names_s, fa);
+ if ( names_p == names + names_s )
+ {
+ cerr << "ERROR: Invalid log functional area spec: '" << fa << "' - skipping\n";
+ continue;
+ }
+
+ size_t nfa = names_p - names;
+
+ if ( nfa != 0 )
+ fas.insert(nfa);
+ }
+
+ return fas;
+}
+
+
+template
+unique_ptr CreateMedium(const string& uri);
+
+class Source
+{
+public:
+ virtual bytevector Read(size_t chunk) = 0;
+ virtual bool IsOpen() = 0;
+ virtual bool End() = 0;
+ static unique_ptr Create(const string& url)
+ {
+ return CreateMedium(url);
+ }
+ virtual ~Source() {}
+};
+
+class Target
+{
+public:
+ virtual void Write(const bytevector& portion) = 0;
+ virtual bool IsOpen() = 0;
+ virtual bool Broken() = 0;
+ static unique_ptr Create(const string& url)
+ {
+ return CreateMedium(url);
+ }
+ virtual ~Target() {}
+};
+
+
+
+map g_options;
+
+string Option(string deflt="") { return deflt; }
+
+template
+string Option(string deflt, string key, Args... further_keys)
+{
+ map::iterator i = g_options.find(key);
+ if ( i == g_options.end() )
+ return Option(deflt, further_keys...);
+ return i->second;
+}
+
+volatile bool int_state = false;
+volatile bool throw_on_interrupt = false;
+bool transmit_verbose = false;
+ostream* cverb = &cout;
+bool bidirectional = false;
+unsigned srt_maxlossttl = 0;
+unsigned stats_report_freq = 0;
+
+void OnINT_SetIntState(int)
+{
+ cerr << "\n-------- REQUESTED INTERRUPT!\n";
+ int_state = true;
+ if ( throw_on_interrupt )
+ throw std::runtime_error("Requested exception interrupt");
+}
+
+void OnAlarm_Interrupt(int)
+{
+ throw std::runtime_error("Watchdog bites hangup");
+}
+
+struct BandwidthGuard
+{
+ typedef std::chrono::steady_clock::time_point time_point;
+ size_t conf_bw;
+ time_point start_time, prev_time;
+ size_t report_count = 0;
+ double average_bw = 0;
+ size_t transfer_size = 0;
+
+ BandwidthGuard(size_t band): conf_bw(band), start_time(std::chrono::steady_clock::now()), prev_time(start_time) {}
+
+ void Checkpoint(size_t size, size_t toreport )
+ {
+ using namespace std::chrono;
+
+ time_point eop = steady_clock::now();
+ auto dur = duration_cast(eop - start_time);
+ //auto this_dur = duration_cast(eop - prev_time);
+
+ transfer_size += size;
+ average_bw = transfer_size*1000000.0/dur.count();
+ //double this_bw = size*1000000.0/this_dur.count();
+
+ if ( toreport )
+ {
+ // Show current bandwidth
+ ++report_count;
+ if ( report_count % toreport == toreport - 1 )
+ {
+ cout.precision(10);
+ int abw = int(average_bw);
+ int abw_trunc = abw/1024;
+ int abw_frac = abw%1024;
+ char bufbw[64];
+ sprintf(bufbw, "%d.%03d", abw_trunc, abw_frac);
+ cout << "+++/+++SRT TRANSFER: " << transfer_size << "B "
+ "DURATION: " << duration_cast(dur).count() << "ms SPEED: " << bufbw << "kB/s\n";
+ }
+ }
+
+ prev_time = eop;
+
+ if ( transfer_size > SIZE_MAX/2 )
+ {
+ transfer_size -= SIZE_MAX/2;
+ start_time = eop;
+ }
+
+ if ( conf_bw == 0 )
+ return; // don't guard anything
+
+ // Calculate expected duration for the given size of bytes (in [ms])
+ double expdur_ms = double(transfer_size)/conf_bw*1000;
+
+ auto expdur = milliseconds(size_t(expdur_ms));
+ // Now compare which is more
+
+ if ( dur >= expdur ) // too slow, but there's nothing we can do. Exit now.
+ return;
+
+ std::this_thread::sleep_for(expdur-dur);
+ }
+};
+
+int bw_report = 0;
+
+extern "C" void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message);
+
+int main( int argc, char** argv )
+{
+ vector args;
+ copy(argv+1, argv+argc, back_inserter(args));
+
+ // Check options
+ vector params;
+
+ for (string a: args)
+ {
+ if ( a[0] == '-' )
+ {
+ string key = a.substr(1);
+ size_t pos = key.find(':');
+ if ( pos == string::npos )
+ pos = key.find(' ');
+ string value = pos == string::npos ? "" : key.substr(pos+1);
+ key = key.substr(0, pos);
+ g_options[key] = value;
+ continue;
+ }
+
+ params.push_back(a);
+ }
+
+ if ( params.size() != 2 )
+ {
+ cerr << "Usage: " << argv[0] << " [options] \n";
+ cerr << "\t-t: - connection timeout\n";
+ cerr << "\t-c: - max size of data read in one step\n";
+ cerr << "\t-b: - set SRT bandwidth\n";
+ cerr << "\t-r: - bandwidth report frequency\n";
+ cerr << "\t-s: - frequency of status report\n";
+ cerr << "\t-k - crash on error (aka developer mode)\n";
+ cerr << "\t-v - verbose mode (prints also size of every data packet passed)\n";
+ return 1;
+ }
+
+ int timeout = stoi(Option("30", "t", "to", "timeout"), 0, 0);
+ size_t chunk = stoul(Option("0", "c", "chunk"), 0, 0);
+ if ( chunk == 0 )
+ chunk = DEFAULT_CHUNK;
+ size_t bandwidth = stoul(Option("0", "b", "bandwidth", "bitrate"), 0, 0);
+ bw_report = stoul(Option("0", "r", "report", "bandwidth-report", "bitrate-report"), 0, 0);
+ transmit_verbose = Option("no", "v", "verbose") != "no";
+ bool crashonx = Option("no", "k", "crash") != "no";
+ bidirectional = Option("no", "2", "rw", "bidirectional") != "no";
+
+ string loglevel = Option("error", "loglevel");
+ string logfa = Option("general", "logfa");
+ string logfile = Option("", "logfile");
+ srt_maxlossttl = stoi(Option("0", "ttl", "max-loss-delay"));
+ stats_report_freq = stoi(Option("0", "s", "stats", "stats-report-frequency"), 0, 0);
+
+ bool internal_log = Option("no", "loginternal") != "no";
+
+ std::ofstream logfile_stream; // leave unused if not set
+
+ srt_setloglevel(ParseLogLevel(loglevel));
+ set fas = ParseLogFA(logfa);
+ for (set::iterator i = fas.begin(); i != fas.end(); ++i)
+ srt_addlogfa(*i);
+
+ char NAME[] = "SRTLIB";
+ if ( internal_log )
+ {
+ srt_setlogflags( 0
+ | SRT_LOGF_DISABLE_TIME
+ | SRT_LOGF_DISABLE_SEVERITY
+ | SRT_LOGF_DISABLE_THREADNAME
+ | SRT_LOGF_DISABLE_EOL
+ );
+ srt_setloghandler(NAME, TestLogHandler);
+ }
+ else if ( logfile != "" )
+ {
+ logfile_stream.open(logfile.c_str());
+ if ( !logfile_stream )
+ {
+ cerr << "ERROR: Can't open '" << logfile << "' for writing - fallback to cerr\n";
+ }
+ else
+ {
+ UDT::setlogstream(logfile_stream);
+ }
+ }
+
+#ifdef WIN32
+#define alarm(argument) (void)0
+#else
+ signal(SIGALRM, OnAlarm_Interrupt);
+#endif
+ signal(SIGINT, OnINT_SetIntState);
+ signal(SIGTERM, OnINT_SetIntState);
+
+ try
+ {
+ auto src = Source::Create(params[0]);
+ auto tar = Target::Create(params[1]);
+
+ // Now loop until broken
+ BandwidthGuard bw(bandwidth);
+
+ if ( transmit_verbose )
+ {
+ cout << "STARTING TRANSMISSION: '" << params[0] << "' --> '" << params[1] << "'\n";
+ }
+
+ extern logging::Logger glog;
+ for (;;)
+ {
+ if ( timeout != -1 )
+ {
+ alarm(timeout);
+ }
+ const bytevector& data = src->Read(chunk);
+ if ( transmit_verbose )
+ cout << " << " << data.size() << " -> ";
+ if ( data.empty() && src->End() )
+ {
+ if ( transmit_verbose )
+ cout << "EOS\n";
+ break;
+ }
+ tar->Write(data);
+ if ( timeout != -1 )
+ {
+ alarm(0);
+ }
+ if ( tar->Broken() )
+ {
+ if ( transmit_verbose )
+ cout << " OUTPUT broken\n";
+ break;
+ }
+ if ( transmit_verbose )
+ cout << " sent\n";
+ if ( int_state )
+ {
+ cerr << "\n (interrupted on request)\n";
+ break;
+ }
+
+ bw.Checkpoint(chunk, bw_report);
+ }
+ alarm(0);
+
+ } catch (...) {
+ if ( crashonx )
+ throw;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+// Class utilities
+
+set true_names = { "1", "yes", "on", "true" };
+set false_names = { "0", "no", "off", "false" };
+
+string udt_status_names [] = {
+"INIT" , "OPENED", "LISTENING", "CONNECTING", "CONNECTED", "BROKEN", "CLOSING", "CLOSED", "NONEXIST"
+};
+
+// Medium concretizations
+
+class FileSource: public Source
+{
+ ifstream ifile;
+public:
+
+ FileSource(const string& path): ifile(path, ios::in | ios::binary) {}
+
+ bytevector Read(size_t chunk) override
+ {
+ bytevector data(chunk);
+ ifile.read(data.data(), chunk);
+ size_t nread = ifile.gcount();
+ if ( nread < data.size() )
+ data.resize(nread);
+ return data;
+ }
+
+ bool IsOpen() override { return bool(ifile); }
+ bool End() override { return ifile.eof(); }
+ //~FileSource() { ifile.close(); }
+};
+
+class FileTarget: public Target
+{
+ ofstream ofile;
+public:
+
+ FileTarget(const string& path): ofile(path, ios::out | ios::trunc | ios::binary) {}
+
+ void Write(const bytevector& data) override
+ {
+ ofile.write(data.data(), data.size());
+ }
+
+ bool IsOpen() override { return !!ofile; }
+ bool Broken() override { return !ofile.good(); }
+ //~FileTarget() { ofile.close(); }
+};
+
+template struct File;
+template <> struct File { typedef FileSource type; };
+template <> struct File { typedef FileTarget type; };
+
+template
+Iface* CreateFile(const string& name) { return new typename File::type (name); }
+
+struct OptionValue
+{
+ string s;
+ union {
+ int i;
+ int64_t l;
+ bool b;
+ };
+
+ const void* value = nullptr;
+ size_t size = 0;
+};
+
+struct SocketOption
+{
+ enum Type { STRING = 0, INT, INT64, BOOL };
+ enum Binding { PRE = 0, POST };
+ enum Domain { SYSTEM, SRT };
+
+ string name;
+ int protocol;
+ int symbol;
+ Type type;
+ Binding binding;
+
+ template
+ bool apply(int socket, string value) const;
+
+ template
+ bool applyt(int socket, string value) const;
+
+ template
+ static int setso(int socket, int protocol, int symbol, const void* data, size_t size);
+
+ template
+ static void extract(string value, OptionValue& val);
+};
+
+template<>
+int SocketOption::setso(int socket, int /*ignored*/, int sym, const void* data, size_t size)
+{
+ return srt_setsockopt(socket, 0, SRT_SOCKOPT(sym), data, size);
+}
+
+template<>
+int SocketOption::setso(int socket, int proto, int sym, const void* data, size_t size)
+{
+ return ::setsockopt(socket, proto, sym, (const char *)data, size);
+}
+
+template<>
+inline void SocketOption::extract(string value, OptionValue& o)
+{
+ o.s = value;
+ o.value = o.s.data();
+ o.size = o.s.size();
+}
+
+template<>
+inline void SocketOption::extract(string value, OptionValue& o)
+{
+ try
+ {
+ o.i = stoi(value, 0, 0);
+ o.value = &o.i;
+ o.size = sizeof o.i;
+ return;
+ }
+ catch (...) // stoi throws
+ {
+ return; // do not change o
+ }
+}
+
+template<>
+inline void SocketOption::extract(string value, OptionValue& o)
+{
+ try
+ {
+ long long vall = stoll(value);
+ o.l = vall; // int64_t resolves to either 'long long', or 'long' being 64-bit integer
+ o.value = &o.l;
+ o.size = sizeof o.l;
+ return ;
+ }
+ catch (...) // stoll throws
+ {
+ return ;
+ }
+}
+
+template<>
+inline void SocketOption::extract(string value, OptionValue& o)
+{
+ bool val;
+ if ( false_names.count(value) )
+ val = false;
+ else if ( true_names.count(value) )
+ val = true;
+ else
+ return;
+
+ o.b = val;
+ o.value = &o.b;
+ o.size = sizeof o.b;
+}
+
+template
+inline bool SocketOption::applyt(int socket, string value) const
+{
+ OptionValue o; // common meet point
+ extract(value, o);
+ int result = setso(socket, protocol, symbol, o.value, o.size);
+ return result != -1;
+}
+
+
+template
+inline bool SocketOption::apply(int socket, string value) const
+{
+ switch ( type )
+ {
+ case STRING: return applyt(socket, value); break;
+ case INT: return applyt(socket, value); break;
+ case INT64: return applyt(socket, value); break;
+ case BOOL: return applyt(socket, value); break;
+ }
+ return false;
+}
+
+SocketOption srt_options [] {
+ { "maxbw", 0, SRTO_MAXBW, SocketOption::INT, SocketOption::PRE },
+ { "pbkeylen", 0, SRTO_PBKEYLEN, SocketOption::INT, SocketOption::PRE },
+ { "passphrase", 0, SRTO_PASSPHRASE, SocketOption::STRING, SocketOption::PRE },
+
+ { "mss", 0, SRTO_MSS, SocketOption::INT, SocketOption::PRE },
+ { "fc", 0, SRTO_FC, SocketOption::INT, SocketOption::PRE },
+ { "sndbuf", 0, SRTO_SNDBUF, SocketOption::INT, SocketOption::PRE },
+ { "rcvbuf", 0, SRTO_RCVBUF, SocketOption::INT, SocketOption::PRE },
+ { "ipttl", 0, SRTO_IPTTL, SocketOption::INT, SocketOption::PRE },
+ { "iptos", 0, SRTO_IPTOS, SocketOption::INT, SocketOption::PRE },
+ { "inputbw", 0, SRTO_INPUTBW, SocketOption::INT64, SocketOption::POST },
+ { "oheadbw", 0, SRTO_OHEADBW, SocketOption::INT, SocketOption::POST },
+ { "tsbpddelay", 0, SRTO_TSBPDDELAY, SocketOption::INT, SocketOption::PRE },
+ { "tsbpdmaxlag", 0, SRTO_TSBPDMAXLAG, SocketOption::INT, SocketOption::PRE },
+ { "nakreport", 0, SRTO_NAKREPORT, SocketOption::BOOL, SocketOption::PRE },
+ { "conntimeo", 0, SRTO_CONNTIMEO, SocketOption::INT, SocketOption::PRE }
+};
+
+class SrtCommon
+{
+ int srt_conn_epoll = -1;
+protected:
+
+ bool m_output_direction = false; //< Defines which of SND or RCV option variant should be used, also to set SRT_SENDER for output
+ bool m_blocking_mode = true; //< enforces using SRTO_SNDSYN or SRTO_RCVSYN, depending on @a m_output_direction
+ int m_timeout = 0; //< enforces using SRTO_SNDTIMEO or SRTO_RCVTIMEO, depending on @a m_output_direction
+ bool m_tsbpdmode = true;
+ map m_options; // All other options, as provided in the URI
+ SRTSOCKET m_sock = SRT_INVALID_SOCK;
+ SRTSOCKET m_bindsock = SRT_INVALID_SOCK;
+ bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; }
+ bool IsBroken() { return srt_getsockstate(m_sock) > SRTS_CONNECTED; }
+
+ void Init(string host, int port, map par, bool dir_output)
+ {
+ m_output_direction = dir_output;
+
+ // Application-specific options: mode, blocking, timeout, adapter
+ if ( transmit_verbose )
+ {
+ cout << "Parameters:\n";
+ for (map::iterator i = par.begin(); i != par.end(); ++i)
+ {
+ cout << "\t" << i->first << " = '" << i->second << "'\n";
+ }
+ }
+
+ string mode = "default";
+ if ( par.count("mode") )
+ mode = par.at("mode");
+
+ if ( mode == "default" )
+ {
+ // Use the following convention:
+ // 1. Server for source, Client for target
+ // 2. If host is empty, then always server.
+ if ( host == "" )
+ mode = "server";
+ //else if ( !dir_output )
+ //mode = "server";
+ else
+ mode = "client";
+ }
+ par.erase("mode");
+
+ if ( par.count("blocking") )
+ {
+ m_blocking_mode = !false_names.count(par.at("blocking"));
+ par.erase("blocking");
+ }
+
+ if ( par.count("timeout") )
+ {
+ m_timeout = stoi(par.at("timeout"), 0, 0);
+ par.erase("timeout");
+ }
+
+ string adapter = ""; // needed for rendezvous only
+ if ( par.count("adapter") )
+ {
+ adapter = par.at("adapter");
+ par.erase("adapter");
+ }
+
+ if ( par.count("tsbpd") && false_names.count(par.at("tsbpd")) )
+ {
+ m_tsbpdmode = false;
+ }
+
+ // Assign the others here.
+ m_options = par;
+
+ if ( transmit_verbose )
+ cout << "Opening SRT " << (dir_output ? "target" : "source") << " " << mode
+ << "(" << (m_blocking_mode ? "" : "non-") << "blocking)"
+ << " on " << host << ":" << port << endl;
+
+ if ( mode == "client" || mode == "caller" )
+ OpenClient(host, port);
+ else if ( mode == "server" || mode == "listener" )
+ OpenServer(host == "" ? adapter : host, port);
+ else if ( mode == "rendezvous" )
+ OpenRendezvous(adapter, host, port);
+ else
+ {
+ throw std::invalid_argument("Invalid 'mode'. Use 'client' or 'server'");
+ }
+ }
+
+ int AddPoller(SRTSOCKET socket, int modes)
+ {
+ int pollid = srt_epoll_create();
+ if ( pollid == -1 )
+ throw std::runtime_error("Can't create epoll in nonblocking mode");
+ srt_epoll_add_usock(pollid, socket, &modes);
+ return pollid;
+ }
+
+ virtual int ConfigurePost(SRTSOCKET sock)
+ {
+ bool yes = m_blocking_mode;
+ int result = 0;
+ if ( m_output_direction )
+ {
+ result = srt_setsockopt(sock, 0, SRTO_SNDSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_timeout )
+ return srt_setsockopt(sock, 0, SRTO_SNDTIMEO, &m_timeout, sizeof m_timeout);
+ }
+ else
+ {
+ result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_timeout )
+ return srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout);
+ }
+
+ for (auto o: srt_options)
+ {
+ if ( o.binding == SocketOption::POST && m_options.count(o.name) )
+ {
+ string value = m_options.at(o.name);
+ bool ok = o.apply(sock, value);
+ if ( transmit_verbose )
+ {
+ if ( !ok )
+ cout << "WARNING: failed to set '" << o.name << "' (post, " << (m_output_direction? "target":"source") << ") to " << value << endl;
+ else
+ cout << "NOTE: SRT/post::" << o.name << "=" << value << endl;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ virtual int ConfigurePre(SRTSOCKET sock)
+ {
+ int result = 0;
+
+ int yes = 1;
+ if ( m_tsbpdmode )
+ {
+ result = srt_setsockopt(sock, 0, SRTO_TSBPDMODE, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+ }
+
+ if ( ::srt_maxlossttl != 0 )
+ {
+ result = srt_setsockopt(sock, 0, SRTO_LOSSMAXTTL, &srt_maxlossttl, sizeof srt_maxlossttl);
+ if ( result == -1 )
+ return result;
+ }
+
+ if ( m_options.count("passphrase") )
+ {
+ if ( transmit_verbose )
+ cout << "NOTE: using passphrase and 16-bit key\n";
+
+ // Insert default
+ if ( m_options.count("pbkeylen") == 0 )
+ {
+ m_options["pbkeylen"] = m_output_direction ? "16" : "0";
+ }
+ }
+
+ // Let's pretend async mode is set this way.
+ // This is for asynchronous connect.
+ yes = m_blocking_mode;
+ result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ //if ( m_timeout )
+ // result = srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout);
+ //if ( result == -1 )
+ // return result;
+
+ if ( transmit_verbose )
+ {
+ cout << "PRE: blocking mode set: " << yes << " timeout " << m_timeout << endl;
+ }
+
+ for (auto o: srt_options)
+ {
+ if ( o.binding == SocketOption::PRE && m_options.count(o.name) )
+ {
+ string value = m_options.at(o.name);
+ bool ok = o.apply(sock, value);
+ if ( transmit_verbose )
+ {
+ if ( !ok )
+ cout << "WARNING: failed to set '" << o.name << "' (pre, " << (m_output_direction? "target":"source") << ") to " << value << endl;
+ else
+ cout << "NOTE: SRT/pre::" << o.name << "=" << value << endl;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ void OpenClient(string host, int port)
+ {
+ m_sock = srt_socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_sock == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_socket");
+
+ int stat = ConfigurePre(m_sock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+
+ if ( !m_blocking_mode )
+ {
+ srt_conn_epoll = AddPoller(m_sock, SRT_EPOLL_OUT);
+ }
+
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( transmit_verbose )
+ {
+ cout << "Connecting to " << host << ":" << port << " ... ";
+ cout.flush();
+ }
+ stat = srt_connect(m_sock, psa, sizeof sa);
+ if ( stat == SRT_ERROR )
+ {
+ Error(UDT::getlasterror(), "UDT::connect");
+ }
+ if ( !m_blocking_mode )
+ {
+ if ( transmit_verbose )
+ cout << "[ASYNC] " << flush;
+
+ /* SPIN-WAITING version. Don't use it unless you know what you're doing.
+
+ for (;;)
+ {
+ int state = UDT::getsockstate(m_sock);
+ if ( state < CONNECTED )
+ {
+ if ( verbose )
+ cout << state << flush;
+ usleep(250000);
+ continue;
+ }
+ else if ( state > CONNECTED )
+ {
+ Error(UDT::getlasterror(), "UDT::connect status=" + udt_status_names[state]);
+ }
+
+ stat = 0; // fake that connect() returned 0
+ break;
+ }
+ */
+
+ // Socket readiness for connection is checked by polling on WRITE allowed sockets.
+ int len = 2;
+ SRTSOCKET ready[2];
+ if ( srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, -1, 0, 0, 0, 0) != -1 )
+ {
+ if ( transmit_verbose )
+ {
+ cout << "[EPOLL: " << len << " sockets] " << flush;
+ }
+ }
+ else
+ {
+ Error(UDT::getlasterror(), "srt_epoll_wait");
+ }
+ }
+
+ if ( transmit_verbose )
+ cout << " connected.\n";
+ stat = ConfigurePost(m_sock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ void Error(UDT::ERRORINFO& udtError, string src)
+ {
+ int udtResult = udtError.getErrorCode();
+ if ( transmit_verbose )
+ cout << "FAILURE\n" << src << ": [" << udtResult << "] " << udtError.getErrorMessage() << endl;
+ udtError.clear();
+ throw std::invalid_argument("error in " + src);
+ }
+
+ void OpenServer(string host, int port)
+ {
+ m_bindsock = srt_socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_bindsock == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_socket");
+
+ int stat = ConfigurePre(m_bindsock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+
+ if ( !m_blocking_mode )
+ {
+ srt_conn_epoll = AddPoller(m_bindsock, SRT_EPOLL_OUT);
+ }
+
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( transmit_verbose )
+ {
+ cout << "Binding a server on " << host << ":" << port << " ...";
+ cout.flush();
+ }
+ stat = srt_bind(m_bindsock, psa, sizeof sa);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_bind");
+ if ( transmit_verbose )
+ {
+ cout << " listen... ";
+ cout.flush();
+ }
+ stat = srt_listen(m_bindsock, 1);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_listen");
+
+ sockaddr_in scl;
+ int sclen = sizeof scl;
+ if ( transmit_verbose )
+ {
+ cout << " accept... ";
+ cout.flush();
+ }
+ ::throw_on_interrupt = true;
+
+ if ( !m_blocking_mode )
+ {
+ if ( transmit_verbose )
+ cout << "[ASYNC] " << flush;
+
+ int len = 2;
+ SRTSOCKET ready[2];
+ if ( srt_epoll_wait(srt_conn_epoll, 0, 0, ready, &len, -1, 0, 0, 0, 0) == -1 )
+ Error(UDT::getlasterror(), "srt_epoll_wait");
+
+ if ( transmit_verbose )
+ {
+ cout << "[EPOLL: " << len << " sockets] " << flush;
+ }
+ }
+
+ m_sock = srt_accept(m_bindsock, (sockaddr*)&scl, &sclen);
+ if ( m_sock == SRT_INVALID_SOCK )
+ Error(UDT::getlasterror(), "srt_accept");
+
+ if ( transmit_verbose )
+ cout << " connected.\n";
+ ::throw_on_interrupt = false;
+
+ // ConfigurePre is done on bindsock, so any possible Pre flags
+ // are DERIVED by sock. ConfigurePost is done exclusively on sock.
+ stat = ConfigurePost(m_sock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ void OpenRendezvous(string adapter, string host, int port)
+ {
+ m_sock = srt_socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_sock == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_socket");
+
+ bool yes = true;
+ srt_setsockopt(m_sock, 0, SRTO_RENDEZVOUS, &yes, sizeof yes);
+
+ int stat = ConfigurePre(m_sock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+
+ if ( !m_blocking_mode )
+ {
+ srt_conn_epoll = AddPoller(m_bindsock, SRT_EPOLL_OUT);
+ }
+
+ sockaddr_in localsa = CreateAddrInet(adapter, port);
+ sockaddr* plsa = (sockaddr*)&localsa;
+ if ( transmit_verbose )
+ {
+ cout << "Binding a server on " << adapter << ":" << port << " ...";
+ cout.flush();
+ }
+ stat = srt_bind(m_sock, plsa, sizeof localsa);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_bind");
+
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( transmit_verbose )
+ {
+ cout << "Connecting to " << host << ":" << port << " ... ";
+ cout.flush();
+ }
+ stat = srt_connect(m_sock, psa, sizeof sa);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_connect");
+ if ( transmit_verbose )
+ cout << " connected.\n";
+
+ stat = ConfigurePost(m_sock);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ ~SrtCommon()
+ {
+ if ( transmit_verbose )
+ cout << "SrtCommon: DESTROYING CONNECTION, closing sockets\n";
+ if ( m_sock != UDT::INVALID_SOCK )
+ srt_close(m_sock);
+
+ if ( m_bindsock != UDT::INVALID_SOCK )
+ srt_close(m_bindsock);
+ }
+};
+
+class SrtSource: public Source, public SrtCommon
+{
+ int srt_epoll = -1;
+public:
+
+ SrtSource(string host, int port, const map& par)
+ {
+ Init(host, port, par, false);
+
+ if ( !m_blocking_mode )
+ {
+ srt_epoll = AddPoller(m_sock, SRT_EPOLL_IN);
+ }
+ }
+
+ bytevector Read(size_t chunk) override
+ {
+ static size_t counter = 1;
+
+ bytevector data(chunk);
+ bool ready = true;
+ int stat;
+ do
+ {
+ ::throw_on_interrupt = true;
+ stat = srt_recvmsg(m_sock, data.data(), chunk);
+ ::throw_on_interrupt = false;
+ if ( stat == SRT_ERROR )
+ {
+ if ( !m_blocking_mode )
+ {
+ // EAGAIN for SRT READING
+ if ( srt_getlasterror(NULL) == SRT_EASYNCRCV )
+ {
+ if ( transmit_verbose )
+ {
+ cout << "AGAIN: - waiting for data by epoll...\n";
+ }
+ // Poll on this descriptor until reading is available, indefinitely.
+ int len = 2;
+ SRTSOCKET ready[2];
+ if ( srt_epoll_wait(srt_epoll, ready, &len, 0, 0, -1, 0, 0, 0, 0) != -1 )
+ {
+ if ( transmit_verbose )
+ {
+ cout << "... epoll reported ready " << len << " sockets\n";
+ }
+ continue;
+ }
+ // If was -1, then passthru.
+ }
+ }
+ Error(UDT::getlasterror(), "recvmsg");
+ return bytevector();
+ }
+
+ if ( stat == 0 )
+ {
+ // Not necessarily eof. Closed connection is reported as error.
+ this_thread::sleep_for(chrono::milliseconds(10));
+ ready = false;
+ }
+ }
+ while (!ready);
+
+ chunk = size_t(stat);
+ if ( chunk < data.size() )
+ data.resize(chunk);
+
+ CBytePerfMon perf;
+ srt_bstats(m_sock, &perf, true);
+ if ( bw_report && int(counter % bw_report) == bw_report - 1 )
+ {
+ cout << "+++/+++SRT BANDWIDTH: " << perf.mbpsBandwidth << endl;
+ }
+
+ if ( stats_report_freq && counter % stats_report_freq == stats_report_freq - 1)
+ {
+ CPerfMon pmon;
+ memset(&pmon, 0, sizeof pmon);
+ UDT::perfmon(m_sock, &pmon, false);
+ PrintSrtStats(m_sock, pmon);
+ }
+
+ ++counter;
+
+ return data;
+ }
+
+ virtual int ConfigurePre(UDTSOCKET sock) override
+ {
+ int result = SrtCommon::ConfigurePre(sock);
+ if ( result == -1 )
+ return result;
+ // For sending party, the SRT_SENDER flag must be set, otherwise
+ // the connection will be pure UDT.
+ int yes = 1;
+
+ if ( ::bidirectional )
+ {
+ result = srt_setsockopt(sock, 0, SRTO_TWOWAYDATA, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+ }
+
+ return 0;
+ }
+
+ bool IsOpen() override { return IsUsable(); }
+ bool End() override { return IsBroken(); }
+};
+
+class SrtTarget: public Target, public SrtCommon
+{
+ int srt_epoll = -1;
+public:
+
+ SrtTarget(string host, int port, const map& par)
+ {
+ Init(host, port, par, true);
+ }
+
+ virtual int ConfigurePre(SRTSOCKET sock) override
+ {
+ int result = SrtCommon::ConfigurePre(sock);
+ if ( result == -1 )
+ return result;
+ // For sending party, the SRT_SENDER flag must be set, otherwise
+ // the connection will be pure UDT.
+ int yes = 1;
+
+ if ( ::bidirectional )
+ {
+ result = srt_setsockopt(sock, 0, SRTO_TWOWAYDATA, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+ }
+ else
+ {
+ result = srt_setsockopt(sock, 0, SRTO_SENDER, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+ }
+
+ return 0;
+ }
+
+ void Write(const bytevector& data) override
+ {
+ ::throw_on_interrupt = true;
+
+ // Check first if it's ready to write.
+ // If not, wait indefinitely.
+ if ( !m_blocking_mode )
+ {
+ int ready[2];
+ int len = 2;
+ if ( srt_epoll_wait(srt_epoll, 0, 0, ready, &len, -1, 0, 0, 0, 0) == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_epoll_wait");
+ }
+
+ int stat = srt_sendmsg2(m_sock, data.data(), data.size(), nullptr);
+ if ( stat == SRT_ERROR )
+ Error(UDT::getlasterror(), "srt_sendmsg");
+ ::throw_on_interrupt = false;
+ }
+
+ bool IsOpen() override { return IsUsable(); }
+ bool Broken() override { return IsBroken(); }
+
+};
+
+template struct Srt;
+template <> struct Srt { typedef SrtSource type; };
+template <> struct Srt { typedef SrtTarget type; };
+
+template
+Iface* CreateSrt(const string& host, int port, const map& par) { return new typename Srt::type (host, port, par); }
+
+class ConsoleSource: public Source
+{
+public:
+
+ ConsoleSource()
+ {
+ }
+
+ bytevector Read(size_t chunk) override
+ {
+ bytevector data(chunk);
+ bool st = cin.read(data.data(), chunk).good();
+ chunk = cin.gcount();
+ if ( chunk == 0 && !st )
+ return bytevector();
+
+ if ( chunk < data.size() )
+ data.resize(chunk);
+
+ return data;
+ }
+
+ bool IsOpen() override { return cin.good(); }
+ bool End() override { return cin.eof(); }
+};
+
+class ConsoleTarget: public Target
+{
+public:
+
+ ConsoleTarget()
+ {
+ }
+
+ void Write(const bytevector& data) override
+ {
+ cout.write(data.data(), data.size());
+ }
+
+ bool IsOpen() override { return cout.good(); }
+ bool Broken() override { return cout.eof(); }
+};
+
+template struct Console;
+template <> struct Console { typedef ConsoleSource type; };
+template <> struct Console { typedef ConsoleTarget type; };
+
+template
+Iface* CreateConsole() { return new typename Console::type (); }
+
+
+// More options can be added in future.
+SocketOption udp_options [] {
+ { "ipttl", IPPROTO_IP, IP_TTL, SocketOption::INT, SocketOption::PRE },
+ { "iptos", IPPROTO_IP, IP_TOS, SocketOption::INT, SocketOption::PRE },
+};
+
+class UdpCommon
+{
+protected:
+ int m_sock = -1;
+ sockaddr_in sadr;
+ string adapter;
+ map m_options;
+
+ void Setup(string host, int port, map attr)
+ {
+ m_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ sadr = CreateAddrInet(host, port);
+
+ if ( attr.count("multicast") )
+ {
+ adapter = attr.count("adapter") ? attr.at("adapter") : string();
+ sockaddr_in maddr;
+ if ( adapter == "" )
+ {
+ maddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else
+ {
+ maddr = CreateAddrInet(adapter, port);
+ }
+
+ ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = sadr.sin_addr.s_addr;
+ mreq.imr_interface.s_addr = maddr.sin_addr.s_addr;
+#ifdef WIN32
+ const char* mreq_arg = (const char*)&mreq;
+ const auto status_error = SOCKET_ERROR;
+#else
+ const void* mreq_arg = &mreq;
+ const auto status_error = -1;
+#endif
+ int res = setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, mreq_arg, sizeof(mreq));
+
+ if ( res == status_error )
+ {
+ throw runtime_error("adding to multicast membership failed");
+ }
+ attr.erase("multicast");
+ attr.erase("adapter");
+ }
+
+ m_options = attr;
+
+ for (auto o: udp_options)
+ {
+ // Ignore "binding" - for UDP there are no post options.
+ if ( m_options.count(o.name) )
+ {
+ string value = m_options.at(o.name);
+ bool ok = o.apply(m_sock, value);
+ if ( transmit_verbose && !ok )
+ cout << "WARNING: failed to set '" << o.name << "' to " << value << endl;
+ }
+ }
+ }
+
+ ~UdpCommon()
+ {
+#ifdef WIN32
+ if (m_sock != -1)
+ {
+ shutdown(m_sock, SD_BOTH);
+ closesocket(m_sock);
+ m_sock = -1;
+ }
+#else
+ close(m_sock);
+#endif
+ }
+};
+
+
+class UdpSource: public Source, public UdpCommon
+{
+ bool eof = true;
+public:
+
+ UdpSource(string host, int port, const map& attr)
+ {
+ Setup(host, port, attr);
+ int stat = ::bind(m_sock, (sockaddr*)&sadr, sizeof sadr);
+ if ( stat == -1 )
+ {
+ perror("bind");
+ throw runtime_error("bind failed, UDP cannot read");
+ }
+ eof = false;
+ }
+
+ bytevector Read(size_t chunk) override
+ {
+ bytevector data(chunk);
+ sockaddr_in sa;
+ socklen_t si;
+ int stat = recvfrom(m_sock, data.data(), chunk, 0, (sockaddr*)&sa, &si);
+ if ( stat == -1 || stat == 0 )
+ {
+ eof = true;
+ return bytevector();
+ }
+
+ chunk = size_t(stat);
+ if ( chunk < data.size() )
+ data.resize(chunk);
+
+ return data;
+ }
+
+ bool IsOpen() override { return m_sock != -1; }
+ bool End() override { return eof; }
+};
+
+class UdpTarget: public Target, public UdpCommon
+{
+public:
+ UdpTarget(string host, int port, const map& attr )
+ {
+ Setup(host, port, attr);
+ }
+
+ void Write(const bytevector& data) override
+ {
+ int stat = sendto(m_sock, data.data(), data.size(), 0, (sockaddr*)&sadr, sizeof sadr);
+ if ( stat == -1 )
+ {
+ perror("UdpTarget: write");
+ throw runtime_error("Error during write");
+ }
+ }
+
+ bool IsOpen() override { return m_sock != -1; }
+ bool Broken() override { return false; }
+};
+
+template struct Udp;
+template <> struct Udp { typedef UdpSource type; };
+template <> struct Udp { typedef UdpTarget type; };
+
+template
+Iface* CreateUdp(const string& host, int port, const map& par) { return new typename Udp::type (host, port, par); }
+
+template
+inline bool IsOutput() { return false; }
+
+template<>
+inline bool IsOutput() { return true; }
+
+template
+unique_ptr CreateMedium(const string& uri)
+{
+ unique_ptr ptr;
+
+ UriParser u(uri);
+
+ int iport = 0;
+ switch ( u.type() )
+ {
+ default: ; // do nothing, return nullptr
+ case UriParser::FILE:
+ if ( u.host() == "con" || u.host() == "console" )
+ {
+ if ( IsOutput () && (
+ (transmit_verbose && cverb == &cout)
+ || bw_report) )
+ {
+ cerr << "ERROR: file://con with -v or -r would result in mixing the data and text info.\n";
+ cerr << "ERROR: HINT: you can stream through a FIFO (named pipe)\n";
+ throw invalid_argument("incorrect parameter combination");
+ }
+ ptr.reset( CreateConsole () );
+ }
+ else
+ ptr.reset( CreateFile (u.path()));
+ break;
+
+
+ case UriParser::SRT:
+#if !DEVELOPER_MODE
+ if ( IsOutput () )
+ {
+ cerr << "SRT output not supported\n";
+ throw invalid_argument("incorrect output");
+ }
+#endif
+ iport = atoi(u.port().c_str());
+ if ( iport <= 1024 )
+ {
+ cerr << "Port value invalid: " << iport << " - must be >1024\n";
+ throw invalid_argument("Invalid port number");
+ }
+ ptr.reset( CreateSrt (u.host(), iport, u.parameters()) );
+ break;
+
+
+ case UriParser::UDP:
+ iport = atoi(u.port().c_str());
+ if ( iport <= 1024 )
+ {
+ cerr << "Port value invalid: " << iport << " - must be >1024\n";
+ throw invalid_argument("Invalid port number");
+ }
+ ptr.reset( CreateUdp (u.host(), iport, u.parameters()) );
+ break;
+
+ }
+
+ return ptr;
+}
+
+
+void TestLogHandler(void* opaque, int level, const char* file, int line, const char* area, const char* message)
+{
+ char prefix[100] = "";
+ if ( opaque )
+ strncpy(prefix, (char*)opaque, 99);
+ time_t now;
+ time(&now);
+ char buf[1024];
+ struct tm local;
+ localtime_r(&now, &local);
+ size_t pos = strftime(buf, 1024, "[%c ", &local);
+ snprintf(buf+pos, 1024-pos, "%s:%d(%s)]{%d} %s", file, line, area, level, message);
+
+ cerr << buf << endl;
+}
+
diff --git a/apps/suflip.cpp b/apps/suflip.cpp
new file mode 100644
index 000000000..6db1af3fd
--- /dev/null
+++ b/apps/suflip.cpp
@@ -0,0 +1,689 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+// This is a simplified version of stransmit, which does not use C++11,
+// however its functionality is limited to SRT to UDT only.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "appcommon.hpp"
+#include "uriparser.hpp"
+
+
+using namespace std;
+
+typedef std::vector bytevector;
+
+string true_names_i [] = { "1", "yes", "on", "true" };
+string false_names_i [] = { "0", "no", "off", "false" };
+
+set true_names, false_names;
+
+struct InitializeMe
+{
+ InitializeMe()
+ {
+ copy(true_names_i, true_names_i+4, inserter(true_names, true_names.begin()));
+ copy(false_names_i, false_names_i+4, inserter(false_names, false_names.begin()));
+ }
+
+} g_moron;
+
+bool verbose = false;
+volatile bool throw_on_interrupt = false;
+
+
+class UdpCommon
+{
+protected:
+ int m_sock;
+ sockaddr_in sadr;
+ string adapter;
+ map m_options;
+
+ UdpCommon():
+ m_sock(-1)
+ {
+ }
+
+ void Setup(string host, int port, map attr)
+ {
+ m_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ sadr = CreateAddrInet(host, port);
+
+ if ( attr.count("multicast") )
+ {
+ adapter = attr.count("adapter") ? attr.at("adapter") : string();
+ sockaddr_in maddr;
+ if ( adapter == "" )
+ {
+ maddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else
+ {
+ maddr = CreateAddrInet(adapter, port);
+ }
+
+ ip_mreq mreq;
+ mreq.imr_multiaddr.s_addr = sadr.sin_addr.s_addr;
+ mreq.imr_interface.s_addr = maddr.sin_addr.s_addr;
+#ifdef WIN32
+ int res = setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq));
+ if ( res == SOCKET_ERROR || res == -1 )
+ {
+ throw runtime_error("adding to multicast membership failed");
+ }
+#else
+ int res = setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if ( res == -1 )
+ {
+ throw runtime_error("adding to multicast membership failed");
+ }
+#endif
+ attr.erase("multicast");
+ attr.erase("adapter");
+ }
+
+ m_options = attr;
+
+ /*
+ for (auto o: udp_options)
+ {
+ // Ignore "binding" - for UDP there are no post options.
+ if ( m_options.count(o.name) )
+ {
+ string value = m_options.at(o.name);
+ bool ok = o.apply(m_sock, value);
+ if ( verbose && !ok )
+ cout << "WARNING: failed to set '" << o.name << "' to " << value << endl;
+ }
+ }
+ */
+ }
+
+ ~UdpCommon()
+ {
+#ifdef WIN32
+ if (m_sock != -1)
+ {
+ shutdown(m_sock, SD_BOTH);
+ closesocket(m_sock);
+ m_sock = -1;
+ }
+#else
+ close(m_sock);
+#endif
+ }
+};
+
+struct Target
+{
+
+};
+
+class UdpTarget: public Target, public UdpCommon
+{
+public:
+ UdpTarget(string host, int port, const map& attr )
+ {
+ Setup(host, port, attr);
+ }
+
+ void Write(const bytevector& data)
+ {
+ int stat = sendto(m_sock, data.data(), data.size(), 0, (sockaddr*)&sadr, sizeof sadr);
+ if ( stat == -1 )
+ {
+ perror("write");
+ throw runtime_error("Error during write");
+ }
+ }
+
+ bool IsOpen() { return m_sock != -1; }
+ bool Broken() { return false; }
+};
+
+
+
+
+class SrtCommon
+{
+protected:
+
+ bool m_output_direction;
+ bool m_blocking_mode;
+ int m_timeout;
+ map m_options; // All other options, as provided in the URI
+ UDTSOCKET m_sock;
+ UDTSOCKET m_bindsock;
+ bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; }
+ bool IsBroken() { return srt_getsockstate(m_sock) > SRTS_CONNECTED; }
+
+ SrtCommon():
+ m_output_direction(false),
+ m_blocking_mode(true),
+ m_timeout(0),
+ m_sock(UDT::INVALID_SOCK),
+ m_bindsock(UDT::INVALID_SOCK)
+ {
+ }
+
+ void Init(string host, int port, map par, bool dir_output)
+ {
+ m_output_direction = dir_output;
+
+ // Application-specific options: mode, blocking, timeout, adapter
+
+ string mode = "default";
+ if ( par.count("mode") )
+ mode = par.at("mode");
+
+ if ( mode == "default" )
+ {
+ // Use the following convention:
+ // 1. Server for source, Client for target
+ // 2. If host is empty, then always server.
+ if ( host == "" )
+ mode = "server";
+ //else if ( !dir_output )
+ //mode = "server";
+ else
+ mode = "client";
+ }
+ par.erase("mode");
+
+ if ( par.count("blocking") )
+ {
+ if ( false_names.count(par.at("blocking")) )
+ {
+ m_blocking_mode = false;
+ }
+ else
+ {
+ m_blocking_mode = true;
+ }
+ }
+
+ par.erase("blocking");
+
+ if ( par.count("timeout") )
+ {
+ m_timeout = atoi(par.at("timeout").c_str());
+ par.erase("timeout");
+ }
+
+ string adapter = ""; // needed for rendezvous only
+ if ( par.count("adapter") )
+ {
+ adapter = par.at("adapter");
+ par.erase("adapter");
+ }
+
+ // Assign the others here.
+ m_options = par;
+
+ if ( verbose )
+ cout << "Opening SRT " << (dir_output ? "target" : "source") << " " << mode
+ << "(" << (m_blocking_mode ? "" : "non-") << "blocking)"
+ << " on " << host << ":" << port << endl;
+
+ if ( mode == "client" || mode == "caller" )
+ OpenClient(host, port);
+ else if ( mode == "server" || mode == "listener" )
+ OpenServer(host == "" ? adapter : host, port);
+ else if ( mode == "rendezvous" )
+ OpenRendezvous(adapter, host, port);
+ else
+ {
+ throw std::invalid_argument("Invalid 'mode'. Use 'client' or 'server'");
+ }
+ }
+
+ virtual int ConfigurePost(UDTSOCKET sock)
+ {
+ bool yes = m_blocking_mode;
+ int result = 0;
+ if ( m_output_direction )
+ {
+ result = UDT::setsockopt(sock, 0, UDT_SNDSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_timeout )
+ return UDT::setsockopt(sock, 0, UDT_SNDTIMEO, &m_timeout, sizeof m_timeout);
+ }
+ else
+ {
+ result = UDT::setsockopt(sock, 0, UDT_RCVSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_timeout )
+ return UDT::setsockopt(sock, 0, UDT_RCVTIMEO, &m_timeout, sizeof m_timeout);
+ }
+
+ /*
+ for (auto o: srt_options)
+ {
+ if ( o.binding == SocketOption::POST && m_options.count(o.name) )
+ {
+ string value = m_options.at(o.name);
+ bool ok = o.apply(sock, value);
+ if ( verbose )
+ {
+ if ( !ok )
+ cout << "WARNING: failed to set '" << o.name << "' (post, " << (m_output_direction? "target":"source") << ") to " << value << endl;
+ else
+ cout << "NOTE: SRT/post::" << o.name << "=" << value << endl;
+ }
+ }
+ }
+ */
+
+ return 0;
+ }
+
+ virtual int ConfigurePre(UDTSOCKET sock)
+ {
+ int result = 0;
+
+ int yes = 1;
+ result = UDT::setsockopt(sock, 0, SRT_TSBPDMODE, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_options.count("passphrase") )
+ {
+ if ( verbose )
+ cout << "NOTE: using passphrase and 16-bit key\n";
+
+ // Insert default
+ if ( m_options.count("pbkeylen") == 0 )
+ {
+ m_options["pbkeylen"] = m_output_direction ? "16" : "0";
+ }
+ }
+
+ // Let's pretend async mode is set this way.
+ // This is for asynchronous connect.
+ yes = m_blocking_mode;
+ result = srt_setsockopt(sock, 0, SRTO_RCVSYN, &yes, sizeof yes);
+ if ( result == -1 )
+ return result;
+
+ if ( m_timeout )
+ return srt_setsockopt(sock, 0, SRTO_RCVTIMEO, &m_timeout, sizeof m_timeout);
+
+ if ( verbose )
+ {
+ cout << "PRE: blocking mode set: " << yes << " timeout " << m_timeout << endl;
+ }
+
+ return 0;
+ }
+
+ void OpenClient(string host, int port)
+ {
+ m_sock = UDT::socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_sock == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::socket");
+
+ int stat = ConfigurePre(m_sock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( verbose )
+ {
+ cout << "Connecting to " << host << ":" << port << " ... ";
+ cout.flush();
+ }
+ stat = UDT::connect(m_sock, psa, sizeof sa);
+ if ( stat == UDT::ERROR )
+ {
+ Error(UDT::getlasterror(), "UDT::connect");
+ }
+ if ( verbose )
+ cout << " connected.\n";
+ stat = ConfigurePost(m_sock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ void Error(UDT::ERRORINFO& udtError, string src)
+ {
+ int udtResult = udtError.getErrorCode();
+ if ( verbose )
+ cout << "FAILURE\n" << src << ": [" << udtResult << "] " << udtError.getErrorMessage() << endl;
+ udtError.clear();
+ throw std::invalid_argument("error in " + src);
+ }
+
+ void OpenServer(string host, int port)
+ {
+ m_bindsock = UDT::socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_bindsock == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::socket");
+
+ int stat = ConfigurePre(m_bindsock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( verbose )
+ {
+ cout << "Binding a server on " << host << ":" << port << " ...";
+ cout.flush();
+ }
+ stat = UDT::bind(m_bindsock, psa, sizeof sa);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::bind");
+ if ( verbose )
+ {
+ cout << " listen... ";
+ cout.flush();
+ }
+ stat = UDT::listen(m_bindsock, 1);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::listen");
+
+ sockaddr_in scl;
+ int sclen = sizeof scl;
+ if ( verbose )
+ {
+ cout << " accept... ";
+ cout.flush();
+ }
+ ::throw_on_interrupt = true;
+ m_sock = UDT::accept(m_bindsock, (sockaddr*)&scl, &sclen);
+ if ( m_sock == UDT::INVALID_SOCK )
+ Error(UDT::getlasterror(), "UDT::accept");
+ if ( verbose )
+ cout << " connected.\n";
+ ::throw_on_interrupt = false;
+
+ // ConfigurePre is done on bindsock, so any possible Pre flags
+ // are DERIVED by sock. ConfigurePost is done exclusively on sock.
+ stat = ConfigurePost(m_sock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ void OpenRendezvous(string adapter, string host, int port)
+ {
+ m_sock = UDT::socket(AF_INET, SOCK_DGRAM, 0);
+ if ( m_sock == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::socket");
+
+ bool yes = true;
+ UDT::setsockopt(m_sock, 0, UDT_RENDEZVOUS, &yes, sizeof yes);
+
+ int stat = ConfigurePre(m_sock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePre");
+
+ sockaddr_in localsa = CreateAddrInet(adapter, port);
+ sockaddr* plsa = (sockaddr*)&localsa;
+ if ( verbose )
+ {
+ cout << "Binding a server on " << adapter << ":" << port << " ...";
+ cout.flush();
+ }
+ stat = UDT::bind(m_sock, plsa, sizeof localsa);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::bind");
+
+ sockaddr_in sa = CreateAddrInet(host, port);
+ sockaddr* psa = (sockaddr*)&sa;
+ if ( verbose )
+ {
+ cout << "Connecting to " << host << ":" << port << " ... ";
+ cout.flush();
+ }
+ stat = UDT::connect(m_sock, psa, sizeof sa);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "UDT::connect");
+ if ( verbose )
+ cout << " connected.\n";
+
+ stat = ConfigurePost(m_sock);
+ if ( stat == UDT::ERROR )
+ Error(UDT::getlasterror(), "ConfigurePost");
+ }
+
+ ~SrtCommon()
+ {
+ if ( verbose )
+ cout << "SrtCommon: DESTROYING CONNECTION, closing sockets\n";
+ if ( m_sock != UDT::INVALID_SOCK )
+ UDT::close(m_sock);
+
+ if ( m_bindsock != UDT::INVALID_SOCK )
+ UDT::close(m_bindsock);
+ }
+};
+
+// Just to seal up
+struct Source
+{
+
+};
+
+class SrtSource: public Source, public SrtCommon
+{
+ int srt_epoll;
+public:
+
+ SrtSource(string host, int port, const map& par)
+ {
+ Init(host, port, par, false);
+
+ if ( !m_blocking_mode )
+ {
+ srt_epoll = srt_epoll_create();
+ if ( srt_epoll == SRT_ERROR )
+ throw std::runtime_error("Can't create epoll in nonblocking mode");
+
+ int modes = SRT_EPOLL_IN;
+ srt_epoll_add_usock(srt_epoll, m_sock, &modes);
+ }
+ }
+
+ bytevector Read(size_t chunk)
+ {
+ bytevector data(chunk);
+ bool ready = true;
+ int stat;
+ do
+ {
+ ::throw_on_interrupt = true;
+ stat = UDT::recvmsg(m_sock, data.data(), chunk);
+ ::throw_on_interrupt = false;
+ if ( stat == UDT::ERROR )
+ {
+ Error(UDT::getlasterror(), "recvmsg");
+ return bytevector();
+ }
+
+ if ( stat == 0 )
+ {
+ // Not necessarily eof. Closed connection is reported as error.
+ //this_thread::sleep_for(chrono::milliseconds(10));
+ usleep(10000);
+ ready = false;
+ }
+ }
+ while (!ready);
+
+ chunk = size_t(stat);
+ if ( chunk < data.size() )
+ data.resize(chunk);
+
+ return data;
+ }
+
+ virtual int ConfigurePre(UDTSOCKET sock)
+ {
+ int result = SrtCommon::ConfigurePre(sock);
+ if ( result == -1 )
+ return result;
+ // For sending party, the SRT_SENDER flag must be set, otherwise
+ // the connection will be pure UDT.
+ //int yes = 1;
+
+
+ return 0;
+ }
+
+ bool IsOpen() { return IsUsable(); }
+ bool End() { return IsBroken(); }
+};
+
+volatile bool int_state = false;
+
+void OnINT_SetIntState(int)
+{
+ cerr << "\n-------- REQUESTED INTERRUPT!\n";
+ int_state = true;
+ if ( throw_on_interrupt )
+ throw std::runtime_error("Requested exception interrupt");
+}
+
+void OnAlarm_Interrupt(int)
+{
+ throw std::runtime_error("Watchdog bites hangup");
+}
+
+
+map g_options;
+
+int main( int argc, char** argv )
+{
+ vector args;
+ copy(argv+1, argv+argc, back_inserter(args));
+
+ // Check options
+ vector params;
+
+ for (vector::iterator ai = args.begin(); ai != args.end(); ++ai)
+ {
+ string& a = *ai;
+
+ if ( a[0] == '-' )
+ {
+ string key = a.substr(1);
+ size_t pos = key.find(':');
+ if ( pos == string::npos )
+ pos = key.find(' ');
+ string value = pos == string::npos ? "" : key.substr(pos+1);
+ key = key.substr(0, pos);
+ g_options[key] = value;
+ continue;
+ }
+
+ params.push_back(a);
+ }
+
+ if ( params.size() != 2 )
+ {
+ cerr << "Usage: " << argv[0] << " [options] \n";
+ return 1;
+ }
+
+ signal(SIGINT, OnINT_SetIntState);
+ signal(SIGTERM, OnINT_SetIntState);
+
+ UriParser su = params[0];
+ UriParser tu = params[1];
+
+ if ( su.scheme() != "srt" || tu.scheme() != "udp" )
+ {
+ cerr << "Source must be srt://... and target must be udp://...\n";
+ return 1;
+ }
+
+ if ( su.portno() < 1024 || tu.portno() < 1024 )
+ {
+ cerr << "Port number must be > 1024\n";
+ return 1;
+ }
+
+ if ( g_options.count("v") )
+ verbose = 1;
+
+ bool crashonx = false;
+
+ const size_t chunk = 1316;
+
+ try
+ {
+ SrtSource src (su.host(), su.portno(), su.parameters());
+ UdpTarget tar (tu.host(), tu.portno(), tu.parameters());
+
+ // Now loop until broken
+ for (;;)
+ {
+ const bytevector& data = src.Read(chunk);
+ if ( verbose )
+ cout << " << " << data.size() << " -> ";
+ if ( data.empty() && src.End() )
+ {
+ if ( verbose )
+ cout << endl;
+ break;
+ }
+ tar.Write(data);
+ if ( tar.Broken() )
+ {
+ if ( verbose )
+ cout << " broken\n";
+ break;
+ }
+ if ( verbose )
+ cout << " sent\n";
+ if ( int_state )
+ {
+ cerr << "\n (interrupted on request)\n";
+ break;
+ }
+
+ }
+
+ } catch (...) {
+ if ( crashonx )
+ throw;
+
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/apps/testcapi.c b/apps/testcapi.c
new file mode 100644
index 000000000..c27913189
--- /dev/null
+++ b/apps/testcapi.c
@@ -0,0 +1,72 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+#include
+#include
+#include
+
+#include
+
+int main( int argc, char** argv )
+{
+ int ss, st;
+ struct sockaddr_in sa;
+ int yes = 1;
+ const char message [] = "This message should be sent to the other side";
+
+ srt_startup();
+
+ ss = srt_socket(AF_INET, SOCK_DGRAM, 0);
+ if ( ss == SRT_ERROR )
+ {
+ fprintf(stderr, "srt_socket: %s\n", srt_getlasterror_str());
+ return 1;
+ }
+
+ sa.sin_port = htons(atoi(argv[2]));
+ if ( inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
+ {
+ return 1;
+ }
+
+ srt_setsockflag(ss, SRTO_SENDER, &yes, sizeof yes);
+
+ st = srt_connect(ss, (struct sockaddr*)&sa, sizeof sa);
+ if ( st == SRT_ERROR )
+ {
+ fprintf(stderr, "srt_connect: %s\n", srt_getlasterror_str());
+ return 1;
+ }
+
+ st = srt_sendmsg2(ss, message, sizeof message, NULL);
+ if ( st == SRT_ERROR )
+ {
+ fprintf(stderr, "srt_sendmsg: %s\n", srt_getlasterror_str());
+ return 1;
+ }
+
+ st = srt_close(ss);
+ if ( st == SRT_ERROR )
+ {
+ fprintf(stderr, "srt_close: %s\n", srt_getlasterror_str());
+ return 1;
+ }
+
+ srt_cleanup();
+ return 0;
+}
diff --git a/apps/uriparser-test.cpp b/apps/uriparser-test.cpp
new file mode 120000
index 000000000..f233f2349
--- /dev/null
+++ b/apps/uriparser-test.cpp
@@ -0,0 +1 @@
+uriparser.cpp
\ No newline at end of file
diff --git a/apps/uriparser.cpp b/apps/uriparser.cpp
new file mode 100644
index 000000000..22aeaa86e
--- /dev/null
+++ b/apps/uriparser.cpp
@@ -0,0 +1,277 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+// STL includes
+#include
+#include
+
+#include "uriparser.hpp"
+
+#ifdef TEST
+#define TEST1 1
+#endif
+
+#ifdef TEST1
+#include
+#endif
+
+
+using namespace std;
+
+
+map types;
+
+struct UriParserInit
+{
+ UriParserInit()
+ {
+ types["file"] = UriParser::FILE;
+ types["srt"] = UriParser::SRT;
+ types["udp"] = UriParser::UDP;
+ types[""] = UriParser::UNKNOWN;
+ }
+} g_uriparser_init;
+
+
+//++
+// UriParser
+//--
+#ifdef TEST1
+
+static void pf(string name, string value)
+{
+ if ( name.substr(0,2) == "m_" )
+ name = name.substr(2);
+ cerr << name << ": " << value << endl;
+}
+
+#define PF(field) pf(#field, field)
+#endif
+
+UriParser::UriParser(const string& strUrl, DefaultExpect exp)
+{
+ Parse(strUrl, exp);
+#ifdef TEST1
+
+ cerr << "PARSED URI: " << m_origUri << endl;
+ PF(m_proto);
+ PF(m_host);
+ PF(m_port);
+ PF(m_path);
+
+ cerr << "SCHEME INDEX: " << int(m_uriType) << endl;
+#endif
+}
+
+UriParser::~UriParser(void)
+{
+}
+
+string UriParser::proto(void)
+{
+ return m_proto;
+}
+
+UriParser::Type UriParser::type()
+{
+ return m_uriType;
+}
+
+string UriParser::host(void)
+{
+ return m_host;
+}
+
+string UriParser::port(void)
+{
+ return m_port;
+}
+
+unsigned short int UriParser::portno(void)
+{
+ // This returns port in numeric version. Fallback to 0.
+ try
+ {
+ int i = atoi(m_port.c_str());
+ if ( i <= 0 || i > 65535 )
+ return 0;
+ return i;
+ }
+ catch (...)
+ {
+ return 0;
+ }
+}
+
+string UriParser::path(void)
+{
+ return m_path;
+}
+
+string UriParser::queryValue(const string& strKey)
+{
+ return m_mapQuery[strKey];
+}
+
+void UriParser::Parse(const string& strUrl, DefaultExpect exp)
+{
+ int iQueryStart = -1;
+
+ size_t idx = strUrl.find("?");
+ if (idx != string::npos)
+ {
+ m_host = strUrl.substr(0, idx);
+ iQueryStart = idx + 1;
+ }
+ else
+ {
+ m_host = strUrl;
+ }
+
+ idx = m_host.find("://");
+ if (idx != string::npos)
+ {
+ m_proto = m_host.substr(0, idx);
+ m_host = m_host.substr(idx + 3, m_host.size() - (idx + 3));
+ }
+
+ idx = m_host.find("/");
+ if (idx != string::npos)
+ {
+ m_path = m_host.substr(idx, m_host.size() - idx);
+ m_host = m_host.substr(0, idx);
+ }
+
+
+ // Check special things in the HOST entry.
+ size_t atp = m_host.find('@');
+ if ( atp != string::npos )
+ {
+ string realhost = m_host.substr(atp+1);
+ string prehost;
+ if ( atp > 0 )
+ {
+ prehost = m_host.substr(0, atp-0);
+ size_t colon = prehost.find(':');
+ if ( colon != string::npos )
+ {
+ string pw = prehost.substr(colon+1);
+ string user;
+ if ( colon > 0 )
+ user = prehost.substr(0, colon-0);
+ m_mapQuery["user"] = user;
+ m_mapQuery["password"] = pw;
+ }
+ else
+ {
+ m_mapQuery["user"] = prehost;
+ }
+ }
+ else
+ {
+ m_mapQuery["multicast"] = "1";
+ }
+ m_host = realhost;
+ }
+
+ idx = m_host.find(":");
+ if (idx != string::npos)
+ {
+ m_port = m_host.substr(idx + 1, m_host.size() - (idx + 1));
+ m_host = m_host.substr(0, idx);
+ }
+
+ if ( m_port == "" && m_host != "" )
+ {
+ // Check if the host-but-no-port has specified
+ // a single integer number. If so
+ // We need to use C86 strtol, cannot use C++11
+ const char* beg = m_host.c_str();
+ const char* end = m_host.c_str() + m_host.size();
+ char* eos = 0;
+ long val = strtol(beg, &eos, 10);
+ if ( val > 0 && eos == end )
+ {
+ m_port = m_host;
+ m_host = "";
+ }
+ }
+
+ string strQueryPair;
+ while (iQueryStart > -1)
+ {
+ idx = strUrl.find("&", iQueryStart);
+ if (idx != string::npos)
+ {
+ strQueryPair = strUrl.substr(iQueryStart, idx - iQueryStart);
+ iQueryStart = idx + 1;
+ }
+ else
+ {
+ strQueryPair = strUrl.substr(iQueryStart, strUrl.size() - iQueryStart);
+ iQueryStart = idx;
+ }
+
+ idx = strQueryPair.find("=");
+ if (idx != string::npos)
+ {
+ m_mapQuery[strQueryPair.substr(0, idx)] = strQueryPair.substr(idx + 1, strQueryPair.size() - (idx + 1));
+ }
+ }
+
+ if ( m_proto == "file" )
+ {
+ if ( m_path.size() > 3 && m_path.substr(0, 3) == "/./" )
+ m_path = m_path.substr(3);
+ }
+
+ // Post-parse fixes
+ // Treat empty protocol as a file. In this case, merge the host and path.
+ if ( exp == EXPECT_FILE && m_proto == "" && m_port == "" )
+ {
+ m_proto = "file";
+ m_path = m_host + m_path;
+ m_host = "";
+ }
+
+ m_uriType = types[m_proto]; // default-constructed UNKNOWN will be used if not found (although also inserted)
+}
+
+#ifdef TEST
+
+using namespace std;
+
+int main( int argc, char** argv )
+{
+ UriParser parser (argv[1]);
+
+ cout << "PARSING URL: " << argv[1] << endl;
+
+ cout << "PROTOCOL: " << parser.proto() << endl;
+ cout << "HOST: " << parser.host() << endl;
+ cout << "PORT: " << parser.portno() << endl;
+ cout << "PATH: " << parser.path() << endl;
+ cout << "PARAMETERS:\n";
+ for (auto p: parser.parameters())
+ cout << "\t" << p.first << " = " << p.second << endl;
+
+
+ return 0;
+}
+#endif
diff --git a/apps/uriparser.hpp b/apps/uriparser.hpp
new file mode 100644
index 000000000..80206bd3b
--- /dev/null
+++ b/apps/uriparser.hpp
@@ -0,0 +1,84 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+#ifndef INC__URL_PARSER_H
+#define INC__URL_PARSER_H
+
+#include
+#include
+#include
+
+
+//++
+// UriParser
+//--
+
+class UriParser
+{
+// Construction
+public:
+
+ enum DefaultExpect { EXPECT_FILE, EXPECT_HOST };
+
+ UriParser(const std::string& strUrl, DefaultExpect exp = EXPECT_FILE);
+ virtual ~UriParser(void);
+
+ // Some predefined types
+ enum Type
+ {
+ UNKNOWN, FILE, UDP, TCP, SRT, RTMP, HTTP
+ };
+ Type type();
+
+// Operations
+public:
+ std::string uri() { return m_origUri; }
+ std::string proto(void);
+ std::string scheme() { return proto(); }
+ std::string host(void);
+ std::string port(void);
+ unsigned short int portno();
+ std::string hostport() { return host() + ":" + port(); }
+ std::string path(void);
+ std::string queryValue(const std::string& strKey);
+ const std::map& parameters() { return m_mapQuery; }
+
+private:
+ void Parse(const std::string& strUrl, DefaultExpect);
+
+// Overridables
+public:
+
+// Overrides
+public:
+
+// Data
+private:
+ std::string m_origUri;
+ std::string m_proto;
+ std::string m_host;
+ std::string m_port;
+ std::string m_path;
+ Type m_uriType;
+
+ std::map m_mapQuery;
+};
+
+//#define TEST1 1
+
+#endif // _FMS_URL_PARSER_H_
diff --git a/apps/utility-test.cpp b/apps/utility-test.cpp
new file mode 100644
index 000000000..295c753ce
--- /dev/null
+++ b/apps/utility-test.cpp
@@ -0,0 +1,47 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+int main( int argc __attribute__((unused)), char** argv __attribute__((unused)))
+{
+ using namespace std;
+
+ cout << "PacketBoundary: " << hex << MSGNO_PACKET_BOUNDARY::mask << endl;
+
+ cout << "PB_FIRST: " << hex << PacketBoundaryBits(PB_FIRST) << endl;
+ cout << "PB_LAST: " << hex << PacketBoundaryBits(PB_LAST) << endl;
+ cout << "PB_SOLO: " << hex << PacketBoundaryBits(PB_SOLO) << endl;
+
+ cout << "inorder: " << hex << MSGNO_PACKET_INORDER::mask << " (1 << " << dec << MSGNO_PACKET_INORDER::offset << ")" << endl;
+ cout << "msgno-seq mask: " << hex << MSGNO_SEQ::mask << endl;
+ cout << "3 wrapped into enckeyspec: " << hex << setw(8) << setfill('0') << MSGNO_ENCKEYSPEC::wrap(3) << " - mask: " << MSGNO_ENCKEYSPEC::mask << endl;
+
+ cout << "SrtVersion test: 2.3.8 == 0x020308 -- SrtVersion(2, 3, 8) == 0x" << hex << setw(8) << setfill('0') << SrtVersion(2, 3, 8) << endl;
+
+ cout << "SEQNO_CONTROL::mask: " << hex << SEQNO_CONTROL::mask << " SEQNO 0x80050000 has control = " << SEQNO_CONTROL::unwrap(0x80050000)
+ << " type = " << SEQNO_MSGTYPE::unwrap(0x80050000) << endl;
+ return 0;
+}
diff --git a/common/srt_compat.c b/common/srt_compat.c
new file mode 100644
index 000000000..3aae4db76
--- /dev/null
+++ b/common/srt_compat.c
@@ -0,0 +1,187 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+// Implementation file for srt_compat.h
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+ *****************************************************************************/
+
+// Prevents from misconfiguration through preprocessor.
+
+#include
+
+#include
+#include
+#if !defined(_WIN32) \
+ && !defined(__MACH__)
+#include
+#endif
+
+#if defined(__MACH__)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+int OSX_clock_gettime(clockid_t clock_id, struct timespec * ts)
+{
+ int result = -1;
+
+ if (ts == NULL)
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ switch (clock_id)
+ {
+ case CLOCK_REALTIME :
+ {
+ struct timeval tv;
+
+ result = gettimeofday(&tv, NULL);
+ if (result == 0)
+ {
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+ }
+ }
+ break;
+
+ case CLOCK_MONOTONIC :
+ {
+ mach_timebase_info_data_t timebase_info;
+ memset(&timebase_info, 0, sizeof(timebase_info));
+ static const uint64_t BILLION = UINT64_C(1000000000);
+
+ (void)mach_timebase_info(&timebase_info);
+ if (timebase_info.numer <= 0
+ || timebase_info.denom <= 0)
+ {
+ result = -1;
+ errno = EINVAL;
+ }
+ else
+ {
+ uint64_t monotonic_nanoseconds = mach_absolute_time()
+ * timebase_info.numer / timebase_info.denom;
+
+ ts->tv_sec = monotonic_nanoseconds / BILLION;
+ ts->tv_nsec = monotonic_nanoseconds % BILLION;
+ if (ts->tv_sec < 0
+ || ts->tv_nsec < 0)
+ {
+ result = -1;
+ errno = EINVAL;
+ }
+ else
+ {
+ result = 0;
+ }
+ }
+ }
+ break;
+
+ #if 0
+ // Warning This is probably slow!!
+ case CLOCK_CALENDAR :
+ {
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ ts->tv_sec = mts.tv_sec;
+ ts->tv_nsec = mts.tv_nsec;
+
+ result = 0;
+ }
+ break;
+ #endif
+
+ default :
+ {
+ result = -1;
+ errno = EINVAL;
+ }
+ }
+
+ return result;
+}
+
+#endif // (__MACH__)
+
+
+extern const char * SysStrError(int errnum, char * buf, size_t buflen)
+{
+ if (buf == NULL || buflen <= 0)
+ {
+ errno = EFAULT;
+ return buf;
+ }
+
+ buf[0] = '\0';
+
+#if defined(_WIN32) || defined(WIN32)
+ LPVOID lpMsgBuf;
+ FormatMessage(0
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ errnum,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf, 0, NULL);
+ char * result = strncpy(buf, (const char *)lpMsgBuf, buflen);
+ LocalFree(lpMsgBuf);
+ return result;
+#elif (!defined(__GNU_LIBRARY__) && !defined(__GLIBC__) ) \
+ || (( (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && ! _GNU_SOURCE )
+ // POSIX/XSI-compliant version.
+ // Overall general POSIX version: returns status.
+ // 0 for success, otherwise it's errno_value or -1 and errno_value is in '::errno'.
+ if (strerror_r(errnum, buf, buflen) != 0)
+ {
+ buf[0] = '\0';
+ }
+ return buf;
+#else
+ // GLIBC is non-standard under these conditions.
+ // GNU version: returns the pointer to the message.
+ // This is either equal to the local buffer (errmsg)
+ // or some system-wide storage, depending on kernel's caprice.
+ char * tBuffer = strerror_r(errnum, buf, buflen);
+ if (tBuffer != NULL
+ && tBuffer != buf)
+ {
+ return strncpy(buf, tBuffer, buflen);
+ }
+ else
+ {
+ return buf;
+ }
+#endif
+}
diff --git a/configure b/configure
new file mode 100755
index 000000000..fe1c2f5b2
--- /dev/null
+++ b/configure
@@ -0,0 +1,207 @@
+#!/usr/bin/tclsh
+
+#
+# SRT - Secure, Reliable, Transport
+# Copyright (c) 2017 Haivision Systems Inc.
+#
+# 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, see
+#
+
+# This is a general-purpose configure script, which is a user-friendly
+# wrapper to call the cmake. Note that --help shows you only the options
+# that are explicitly defined, but which exactly options are really
+# supported, depends on what is defined in CMakeLists.txt. Just every
+# case of --long-option is changed into LONG_OPTION cmake variable.
+# However some of options MENTIONED IN THE HELP TEXT may be handled
+# by the configure script before passing to cmake (and maybe turned
+# into something else).
+#
+# The build-specific option processing is done in configure-data.tcl file.
+#
+# The idea is that CMakeLists.txt contains things that are highly
+# customizable, but no system or option autodetection AWA "sensible
+# defaults" are provided. This is done by this script.
+
+
+set here [file dirname $argv0]
+
+set options ""
+set toolchain_changers ""
+
+source $here/configure-data.tcl
+
+# Update alias with default alias
+dict set alias --prefix --cmake-install-prefix=
+
+proc resolve opt {
+ set type arg
+ set pos [string first $opt =]
+ if { $pos == -1 } {
+ set type bool
+ set mark ""
+ } else {
+ set type arg
+ set mark [string range $opt $pos+1 end]
+ set opt [string range $opt 0 $pos-1]
+ }
+ set var [string toupper [string map {- _ + x} $opt]]
+ return [list --$opt $var $type $mark]
+}
+
+foreach {o desc} $options {
+ lassign [resolve $o] optname optvar opttype optmark
+ set opt($optname) [list $optvar $opttype $optmark]
+ set info($optname) $desc
+}
+
+
+if { $argv == "--help" } {
+ puts stderr "Usage: ./configure \[options\]"
+ puts stderr "OPTIONS:"
+ foreach o [lsort [array names opt]] {
+ lassign $opt($o) unu type mark
+ set imark ""
+ if { $mark != "" } {
+ set imark "=$mark"
+ }
+ puts stderr "\t$o$imark - $info($o)"
+ }
+
+ exit 1
+}
+
+if { [info proc init] != "" } {
+ init
+}
+
+#parray opt
+
+set saveopt ""
+set optkeys ""
+
+set dryrun 0
+set type ""
+
+foreach a $argv {
+ if { [info exists val] } { unset val }
+
+ if { $saveopt != "" } {
+ set optval($saveopt) $a
+ set saveopt ""
+ continue
+ }
+
+ if { [string range $a 0 1] != "--" } {
+ error "Unexpected argument '$a'. Options must start with --"
+ }
+
+ if { $a == "--dryrun" } {
+ set dryrun 1
+ continue
+ }
+
+ set type ""
+
+ if { [string first = $a] != -1 } {
+ lassign [split $a =] a val
+ }
+
+ if { [dict exists $::alias $a] } {
+ set aname [dict get $::alias $a]
+ if { [string first = $aname] != -1 } {
+ lassign [split $aname =] a aval
+ set type arg
+ }
+ }
+
+ if { ![info exists opt($a)] } {
+ #puts stderr "WARNING: Unknown option: $a"
+ # But still, simply turn the option to assign-based use.
+ lassign [resolve [string range $a 2 end]] oname var
+ if { ![info exists val] && $type == "" } {
+ set type bool
+ }
+ } else {
+ lassign $opt($a) var type
+ }
+
+ if { $type == "bool" } {
+ if { ![info exists val] } {
+ set val 1
+ }
+ set optval($a) $val
+ } elseif { [info exists val] } {
+ set optval($a) $val
+ } else {
+ set saveopt $a
+ }
+
+ lappend optkeys $a
+}
+
+if { $saveopt != "" } {
+ error "Extra unhandled argument: $saveopt"
+}
+
+set cmakeopt ""
+
+if { [info proc preprocess] != "" } {
+ preprocess
+}
+
+# Check if there were new values added not added to optkeys
+foreach a [array names optval] {
+ if { $a ni $optkeys } {
+ lappend optkeys $a
+ }
+}
+
+
+foreach a $optkeys {
+
+ if { ![info exists optval($a)] } {
+ continue ;# user action might have removed it.
+ }
+
+ if { ![info exists opt($a)] } {
+ #puts stderr "WARNING: Unknown option: $a"
+ # But still, simply turn the option to assign-based use.
+ lassign [resolve [string range $a 2 end]] oname var
+ if { ![info exists val] && $type == "" } {
+ set type bool
+ }
+ } else {
+ lassign $opt($a) var type
+ }
+
+ set val $optval($a)
+ lappend cmakeopt "-D$var=$val"
+}
+
+
+if { [info proc postprocess] != "" } {
+ postprocess
+}
+
+#puts "VARSPEC: $cmakeopt"
+
+set cmd [list cmake $here {*}$cmakeopt]
+puts "Running: $cmd"
+if { !$dryrun} {
+ if { [catch {exec 2>@stderr >@stdout {*}$cmd} result] } {
+ puts "CONFIGURE: cmake reported error: $result"
+ }
+} else {
+ puts "(not really - dry run)"
+}
diff --git a/configure-data.tcl b/configure-data.tcl
new file mode 100644
index 000000000..12ee33f04
--- /dev/null
+++ b/configure-data.tcl
@@ -0,0 +1,267 @@
+#
+# SRT - Secure, Reliable, Transport
+# Copyright (c) 2017 Haivision Systems Inc.
+#
+# 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, see
+#
+
+# API description:
+
+# Expected variables:
+# - options: dictionary "option-name" : "description"
+# if there's '=' in option name, it expects an argument. Otherwise it's boolean.
+# - alias: optional, you can make shortcuts to longer named options. Remember to use = in target name.
+#
+# Optional procedures:
+# - preprocess: run before command-line arguments ($argv) are reviewed
+# - postprocess: run after options are reviewed and all data filled in
+#
+# Available variables in postprocess:
+#
+# - optval (array): contains all option names with their assigned values
+# - cmakeopt (scalar): a list of all options for "cmake" command line
+
+
+set options {
+ enable-dynamic "compile SRT parts as shared objects (dynamic libraries)"
+ disable-c++11 "turn off parts that require C++11 support"
+ enable-debug "turn on debug+nonoptimized build mode"
+ enable-profile "turn on profile instrumentation"
+ with-compiler-prefix= "set C/C++ toolchains gcc and g++"
+ with-openssl= "Prefix for OpenSSL installation (adds include,lib)"
+ with-openssl-includedir= "Use given path for OpenSSL header files"
+ with-openssl-libdir= "Use given path for OpenSSL library path"
+ with-openssl-libraries= "Use given file list instead of standard -lcrypto"
+ with-openssl-ldflags= "Use given -lDIR values for OpenSSL or absolute library filename"
+ with-pthread-includedir= "Use extra path for pthreads (usually for Windows)"
+ with-pthread-ldflags= "Use specific flags for pthreads (some platforms require -pthread)"
+}
+
+# Just example. Available in the system.
+set alias {
+ --prefix --cmake-install-prefix=
+}
+
+proc pkg-config args {
+ return [string trim [exec pkg-config {*}$args]]
+}
+
+proc flagval v {
+ set out ""
+ foreach o $v {
+ lappend out [string trim [string range $o 2 en]]
+ }
+ return $out
+}
+
+proc preprocess {} {
+
+ # Prepare windows basic path info
+ set ::CYGWIN 0
+ set e [catch {exec uname -o} res]
+ # We have Cygwin, if uname -o returns "cygwin" and does not fail.
+ if { !$e && $res == "Cygwin" } {
+ set ::CYGWIN 1
+ puts "CYGWIN DETECTED"
+ }
+
+ set ::HAVE_LINUX [expr {$::tcl_platform(os) == "Linux"}]
+ set ::HAVE_DARWIN [expr {$::tcl_platform(os) == "Darwin"}]
+
+ set ::CYGWIN_USE_POSIX 0
+ if { "--cygwin-use-posix" in $::optkeys } {
+ set ::CYGWIN_USE_POSIX 1
+ }
+
+ set ::HAVE_WINDOWS 0
+ if { $::tcl_platform(platform) == "windows" } {
+ puts "WINDOWS PLATFORM detected"
+ set ::HAVE_WINDOWS 1
+ }
+
+ if { $::CYGWIN && !$::CYGWIN_USE_POSIX } {
+ puts "CYGWIN - MINGW enforced"
+ # Make Cygwin tools see it right, to compile for MinGW
+
+ if { "--with-compiler-prefix" ni $::optkeys } {
+ set ::optval(--with-compiler-prefix) /bin/x86_64-w64-mingw32-
+ }
+
+ # Extract drive C: information
+ set drive_path [exec mount -p | tail -1 | cut {-d } -f 1]
+ set ::DRIVE_C $drive_path/c
+ set ::HAVE_WINDOWS 1
+ } else {
+
+ # Don't check for Windows, non-Windows parts will not use it.
+ set ::DRIVE_C C:
+ }
+
+}
+
+proc GetCompilerCommand {} {
+ # Expect that the compiler was set through:
+ # --with-compiler-prefix
+ # --cmake-c[++]-compiler
+ # (cmake-toolchain-file will set things up without the need to check things here)
+
+ if { [info exists ::optval(--with-compiler-prefix)] } {
+ set prefix $::optval(--with-compiler-prefix)
+ return ${prefix}gcc
+ }
+
+ if { [info exists ::optval(--cmake-c-compiler)] } {
+ return $::optval(--cmake-c-compiler)
+ }
+
+ if { [info exists ::optval(--cmake-c++-compiler)] } {
+ return $::optval(--cmake-c++-compiler)
+ }
+
+ if { [info exists ::optval(--cmake-cxx-compiler)] } {
+ return $::optval(--cmake-cxx-compiler)
+ }
+
+ puts "NOTE: Cannot obtain compiler, assuming toolchain file will do what's necessary"
+
+ return ""
+}
+
+proc postprocess {} {
+
+ set iscross 0
+
+ # Check if there was any option that changed the toolchain. If so, don't apply any autodetection-based toolchain change.
+ set all_options [array names ::optval]
+ set toolchain_changed no
+ foreach changer {
+ --with-compiler-prefix
+ --cmake-c-compiler
+ --cmake-c++-compiler
+ --cmake-cxx-compiler
+ --cmake-toolchain-file
+ } {
+ if { $changer in $all_options } {
+ puts "NOTE: toolchain changed by '$changer' option"
+ set toolchain_changed yes
+ break
+ }
+ }
+
+ if { $toolchain_changed } {
+ # Check characteristics of the compiler - in particular, whether the target is different
+ # than the current target.
+ set cmd [GetCompilerCommand]
+ if { $cmd != "" } {
+ set gcc_version [exec $cmd -v 2>@1]
+ set target ""
+ foreach l [split $gcc_version \n] {
+ if { [string match Target:* $l] } {
+ set name [lindex $l 1] ;# [0]Target: [1]x86_64-some-things-further
+ set target [lindex [split $name -] 0] ;# [0]x86_64 [1]redhat [2]linux
+ break
+ }
+ }
+
+ if { $target == "" } {
+ puts "NOTE: can't obtain target from gcc -v: $l"
+ } else {
+ if { $target != $::tcl_platform(machine) } {
+ puts "NOTE: foreign target type detected ($target) - setting CROSSCOMPILING flag"
+ lappend ::cmakeopt "-DHAVE_CROSSCOMPILER=1"
+ set iscross 1
+ }
+ }
+ }
+ }
+
+ # Check if --with-openssl and the others are defined.
+
+ set have_openssl 0
+ if { [lsearch -glob $::optkeys --with-openssl*] != -1 } {
+ set have_openssl 1
+ }
+
+ set have_pthread 0
+ if { [lsearch -glob $::optkeys --with-pthread*] != -1 } {
+ set have_pthread 1
+ }
+
+ # Autodetect OpenSSL and pthreads
+ if { $::HAVE_WINDOWS } {
+
+ if { !$have_openssl } {
+ lappend ::cmakeopt "-DWITH_OPENSSL_INCLUDEDIR=$::DRIVE_C/OpenSSL-Win64/include"
+ lappend ::cmakeopt "-DWITH_OPENSSL_LIBDIR=$::DRIVE_C/OpenSSL-Win64/lib/VC/static"
+ lappend ::cmakeopt "-DWITH_OPENSSL_LIBRARIES=libeay32MT.lib ssleay32MT.lib"
+ puts "Adding OpenSSL in $::DRIVE_C/OpenSSL-win64"
+ } else {
+ puts "HAVE_OPENSSL: [lsearch -inline $::optkeys --with-openssl*]"
+ }
+
+
+ if { !$have_pthread } {
+ lappend ::cmakeopt "-DWITH_PTHREAD_INCLUDEDIR=$::DRIVE_C/pthread-win32/include"
+ lappend ::cmakeopt "-DWITH_PTHREAD_LDFLAGS=$::DRIVE_C/pthread-win32/lib/pthread_lib.lib"
+ puts "Adding pthreads in $::DRIVE_C/pthread-win32"
+ } else {
+ puts "HAVE_PTHREADS: [lsearch -inline $::optkeys --with-pthread*]"
+ }
+ }
+
+ if { $::HAVE_LINUX } {
+ # Extract Openssl from pkg-config
+ if { !$have_openssl } {
+ set openssl_libs [pkg-config --libs-only-l --libs-only-other openssl]
+ set openssl_libdir [pkg-config --libs-only-L openssl]
+ set openssl_inc [pkg-config --cflags-only-I openssl]
+ set openssl_prefix [pkg-config --variable=prefix openssl]
+
+ if { $openssl_inc == "" } {
+ # This should be so. And if so, add only these two flags:
+ lappend ::cmakeopt "-DWITH_OPENSSL=$openssl_prefix"
+ } else {
+ lappend ::cmakeopt "-DWITH_OPENSSL_INCLUDEDIR=[flagval $openssl_inc]"
+ }
+
+ if { $openssl_libdir != "" } {
+ lappend ::cmakeopt "-DWITH_OPENSSL_LIBDIR=[flagval $openssl_libdir]"
+ }
+
+ lappend ::cmakeopt "-DWITH_OPENSSL_LDFLAGS=$openssl_libs"
+ }
+
+ if { !$have_pthread } {
+ lappend ::cmakeopt "-DWITH_PTHREAD_LDFLAGS=-lpthread"
+ }
+ }
+
+ if { $::HAVE_DARWIN } {
+ # ON Darwin there's a problem with linking against the Mac-provided OpenSSL.
+ # This must use brew-provided OpenSSL.
+ #
+ if { !$have_openssl } {
+
+ set er [catch {exec brew info openssl} res]
+ if { $er } {
+ error "You must have OpenSSL installed from 'brew' tool. The standard Mac version is inappropriate."
+ }
+
+ lappend ::cmakeopt "-DWITH_OPENSSL_INCLUDEDIR=/usr/local/opt/openssl/include"
+ lappend ::cmakeopt "-DWITH_OPENSSL_LDFLAGS=/usr/local/opt/openssl/lib/libcrypto.a"
+ }
+ }
+
+}
+
diff --git a/haicrypt/HEADERS.maf b/haicrypt/HEADERS.maf
new file mode 100644
index 000000000..10b2c4e3d
--- /dev/null
+++ b/haicrypt/HEADERS.maf
@@ -0,0 +1,8 @@
+# This file is currently reserved for future refactoring, when all headers
+# are going to be moved here. This is the list of headers considered to be
+# attached to the installation package. Once possible, please move the below
+# header files from ../include back to this directory.
+PUBLIC HEADERS
+haicrypt.h
+hcrypt_ctx.h
+hcrypt_msg.h
diff --git a/haicrypt/hc_openssl_aes.c b/haicrypt/hc_openssl_aes.c
new file mode 100644
index 000000000..220b20ca0
--- /dev/null
+++ b/haicrypt/hc_openssl_aes.c
@@ -0,0 +1,479 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include "hcrypt.h"
+
+#ifdef HAICRYPT_USE_OPENSSL_AES
+#include
+#include /* PKCS5_xxx() */
+#include /* AES_xxx() */
+#include
+#include
+
+//#include "hc_openssl_aes.h"
+
+typedef struct tag_hcOpenSSL_AES_data {
+ AES_KEY aes_key[2]; /* even/odd SEK */
+
+#define HCRYPT_OPENSSL_OUTMSGMAX 6
+ uint8_t * outbuf; /* output circle buffer */
+ size_t outbuf_ofs; /* write offset in circle buffer */
+ size_t outbuf_siz; /* circle buffer size */
+} hcOpenSSL_AES_data;
+
+
+#include
+#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) //0.9.8h
+/*
+* AES_wrap_key()/AES_unwrap_key() introduced in openssl 0.9.8h
+* Here is an implementation using AES native API for earlier versions
+*/
+#include
+
+static const unsigned char default_iv[] = {
+ 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6,
+};
+
+int AES_wrap_key(AES_KEY *key, const unsigned char *iv,
+ unsigned char *out,
+ const unsigned char *in, unsigned int inlen)
+ {
+ unsigned char *A, B[16], *R;
+ unsigned int i, j, t;
+ if ((inlen & 0x7) || (inlen < 8))
+ return -1;
+ A = B;
+ t = 1;
+ memcpy(out + 8, in, inlen);
+ if (!iv)
+ iv = default_iv;
+
+ memcpy(A, iv, 8);
+
+ for (j = 0; j < 6; j++)
+ {
+ R = out + 8;
+ for (i = 0; i < inlen; i += 8, t++, R += 8)
+ {
+ memcpy(B + 8, R, 8);
+ AES_encrypt(B, B, key);
+ A[7] ^= (unsigned char)(t & 0xff);
+ if (t > 0xff)
+ {
+ A[6] ^= (unsigned char)((t >> 8) & 0xff);
+ A[5] ^= (unsigned char)((t >> 16) & 0xff);
+ A[4] ^= (unsigned char)((t >> 24) & 0xff);
+ }
+ memcpy(R, B + 8, 8);
+ }
+ }
+ memcpy(out, A, 8);
+ return inlen + 8;
+ }
+
+int AES_unwrap_key(AES_KEY *key, const unsigned char *iv,
+ unsigned char *out,
+ const unsigned char *in, unsigned int inlen)
+ {
+ unsigned char *A, B[16], *R;
+ unsigned int i, j, t;
+ inlen -= 8;
+ if (inlen & 0x7)
+ return -1;
+ if (inlen < 8)
+ return -1;
+ A = B;
+ t = 6 * (inlen >> 3);
+ memcpy(A, in, 8);
+ memcpy(out, in + 8, inlen);
+ for (j = 0; j < 6; j++)
+ {
+ R = out + inlen - 8;
+ for (i = 0; i < inlen; i += 8, t--, R -= 8)
+ {
+ A[7] ^= (unsigned char)(t & 0xff);
+ if (t > 0xff)
+ {
+ A[6] ^= (unsigned char)((t >> 8) & 0xff);
+ A[5] ^= (unsigned char)((t >> 16) & 0xff);
+ A[4] ^= (unsigned char)((t >> 24) & 0xff);
+ }
+ memcpy(B + 8, R, 8);
+ AES_decrypt(B, B, key);
+ memcpy(R, B + 8, 8);
+ }
+ }
+ if (!iv)
+ iv = default_iv;
+ if (memcmp(A, iv, 8))
+ {
+ OPENSSL_cleanse(out, inlen);
+ return 0;
+ }
+ return inlen;
+ }
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
+static unsigned char *hcOpenSSL_AES_GetOutbuf(hcOpenSSL_AES_data *aes_data, size_t pfx_len, size_t out_len)
+{
+ unsigned char *out_buf;
+
+ if ((pfx_len + out_len) > (aes_data->outbuf_siz - aes_data->outbuf_ofs)) {
+ /* Not enough room left, circle buffers */
+ aes_data->outbuf_ofs = 0;
+ }
+ out_buf = &aes_data->outbuf[aes_data->outbuf_ofs];
+ aes_data->outbuf_ofs += (pfx_len + out_len);
+ return(out_buf);
+}
+
+static hcrypt_CipherData *hcOpenSSL_AES_Open(size_t max_len)
+{
+ hcOpenSSL_AES_data *aes_data;
+ unsigned char *membuf;
+ size_t memsiz, padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
+
+ HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL AES\n");
+
+ memsiz = sizeof(*aes_data) + (HCRYPT_OPENSSL_OUTMSGMAX * padded_len);
+ aes_data = malloc(memsiz);
+ if (NULL == aes_data) {
+ HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
+ return(NULL);
+ }
+ membuf = (unsigned char *)aes_data;
+ membuf += sizeof(*aes_data);
+
+ aes_data->outbuf = membuf;
+ aes_data->outbuf_siz = HCRYPT_OPENSSL_OUTMSGMAX * padded_len;
+ aes_data->outbuf_ofs = 0;
+// membuf += aes_data->outbuf_siz;
+
+ return((hcrypt_CipherData *)aes_data);
+}
+
+static int hcOpenSSL_AES_Close(hcrypt_CipherData *cipher_data)
+{
+ if (NULL != cipher_data) {
+ free(cipher_data);
+ }
+ return(0);
+}
+
+static int hcOpenSSL_AES_SetKey(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx, unsigned char *key, size_t key_len)
+{
+ hcOpenSSL_AES_data *aes_data = (hcOpenSSL_AES_data *)cipher_data;
+ AES_KEY *aes_key = &aes_data->aes_key[hcryptCtx_GetKeyIndex(ctx)]; /* Ctx tells if it's for odd or even key */
+
+ if ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) /* Encrypt key */
+ || (ctx->mode == HCRYPT_CTX_MODE_AESCTR)) { /* CTR mode decrypts using encryption methods */
+ if (AES_set_encrypt_key(key, key_len * 8, aes_key)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "AES_set_encrypt_key(sek) failed\n");
+ return(-1);
+ }
+ } else { /* Decrypt key */
+ if (AES_set_decrypt_key(key, key_len * 8, aes_key)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "AES_set_decrypt_key(sek) failed\n");
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+static int hcOpenSSL_AES_Encrypt(
+ hcrypt_CipherData *cipher_data,
+ hcrypt_Ctx *ctx,
+ hcrypt_DataDesc *in_data, int nbin,
+ void *out_p[], size_t out_len_p[], int *nbout_p)
+{
+ hcOpenSSL_AES_data *aes_data = (hcOpenSSL_AES_data *)cipher_data;
+ unsigned char *out_msg;
+ int out_len = 0; //payload size
+ int pfx_len;
+
+ ASSERT(NULL != ctx);
+ ASSERT(NULL != aes_data);
+ ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
+
+ /*
+ * Get message prefix length
+ * to reserve room for unencrypted message header in output buffer
+ */
+ pfx_len = ctx->msg_info->pfx_len;
+
+ /* Get buffer room from the internal circular output buffer */
+ out_msg = hcOpenSSL_AES_GetOutbuf(aes_data, pfx_len, in_data[0].len);
+
+ if (NULL != out_msg) {
+ switch(ctx->mode) {
+ case HCRYPT_CTX_MODE_AESCTR: /* Counter mode */
+ {
+ /* Get current key (odd|even) from context */
+ AES_KEY *aes_key = &aes_data->aes_key[hcryptCtx_GetKeyIndex(ctx)];
+ unsigned char ctr[AES_BLOCK_SIZE];
+ unsigned char iv[AES_BLOCK_SIZE];
+ unsigned blk_ofs = 0;
+
+ /* Get input packet index (in network order) */
+ hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
+
+ /*
+ * Compute the Initial Vector
+ * IV (128-bit):
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | 0s | pki | ctr |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * XOR
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | nonce +
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * pki (32-bit): packet index
+ * ctr (16-bit): block counter
+ * nonce (112-bit): number used once (salt)
+ */
+ memset(&ctr[0], 0, sizeof(ctr));
+ hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
+
+ /* Encrypt packet payload in output message buffer */
+ AES_ctr128_encrypt(in_data[0].payload, &out_msg[pfx_len],
+ in_data[0].len, aes_key, iv, ctr, &blk_ofs);
+
+ /* Prepend packet prefix (clear text) in output buffer */
+ memcpy(out_msg, in_data[0].pfx, pfx_len);
+ /* CTR mode output length is same as input, no padding */
+ out_len = in_data[0].len;
+ break;
+ }
+ case HCRYPT_CTX_MODE_AESECB: /* Electronic Codebook mode (VF-AES) */
+ {
+ int i;
+ int nb = in_data[0].len/AES_BLOCK_SIZE;
+ int nmore = in_data[0].len%AES_BLOCK_SIZE;
+ AES_KEY *aes_key = &aes_data->aes_key[hcryptCtx_GetKeyIndex(ctx)];
+
+ /* Encrypt packet payload, block by block, in output buffer */
+ for (i=0; imsg_info->setPki(out_msg, 16 - nmore);
+ }
+ /* Prepend packet prefix (clear text) in output message buffer */
+ memcpy(out_msg, in_data[0].pfx, pfx_len);
+ /* ECB mode output length is on AES block (128 bits) boundary */
+ out_len = nb * AES_BLOCK_SIZE;
+ break;
+ }
+ case HCRYPT_CTX_MODE_CLRTXT: /* Clear text mode (transparent mode for tests) */
+ memcpy(&out_msg[pfx_len], in_data[0].payload, in_data[0].len);
+ memcpy(out_msg, in_data[0].pfx, pfx_len);
+ out_len = in_data[0].len;
+ break;
+ default:
+ /* Unsupported cipher mode */
+ return(-1);
+ }
+ } else {
+ /* input data too big */
+ return(-1);
+ }
+
+ if (out_len > 0) {
+ /* Encrypted messages have been produced */
+ if (NULL == out_p) {
+ /*
+ * Application did not provided output buffer,
+ * so copy encrypted message back in input buffer
+ */
+ memcpy(in_data[0].pfx, out_msg, pfx_len);
+ memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
+ in_data[0].len = out_len;
+ } else {
+ /*
+ * Set output buffer array to internal circular buffers
+ */
+ out_p[0] = out_msg;
+ out_len_p[0] = pfx_len + out_len;
+ *nbout_p = 1;
+ }
+ } else {
+ /*
+ * Nothing out
+ * This is not an error for implementations using deferred/async processing
+ * with co-processor, DSP, crypto hardware, etc.
+ * Submitted input data could be returned encrypted in a next call.
+ */
+ if (nbout_p != NULL) *nbout_p = 0;
+ return(-1);
+ }
+ return(0);
+}
+
+
+
+static int hcOpenSSL_AES_Decrypt(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx,
+ hcrypt_DataDesc *in_data, int nbin, void *out_p[], size_t out_len_p[], int *nbout_p)
+{
+ hcOpenSSL_AES_data *aes_data = (hcOpenSSL_AES_data *)cipher_data;
+ unsigned char *out_txt;
+ int out_len;
+ int iret = 0;
+
+ ASSERT(NULL != aes_data);
+ ASSERT(NULL != ctx);
+ ASSERT((NULL != in_data) || (1 == nbin)); //Only one in_data[] supported
+
+ /* Reserve output buffer (w/no header) */
+ out_txt = hcOpenSSL_AES_GetOutbuf(aes_data, 0, in_data[0].len);
+
+ if (NULL != out_txt) {
+ switch(ctx->mode) {
+ case HCRYPT_CTX_MODE_AESCTR:
+ {
+ /* Get current key (odd|even) from context */
+ AES_KEY *aes_key = &aes_data->aes_key[hcryptCtx_GetKeyIndex(ctx)];
+ unsigned char ctr[AES_BLOCK_SIZE];
+ unsigned char iv[AES_BLOCK_SIZE];
+ unsigned blk_ofs = 0;
+
+ /* Get input message index (in network order) */
+ hcrypt_Pki pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
+
+ /*
+ * Compute the Initial Vector
+ * IV (128-bit):
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | 0s | pki | ctr |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * XOR
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | nonce +
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * pki (32-bit): packet index
+ * ctr (16-bit): block counter
+ * nonce (112-bit): number used once (salt)
+ */
+ memset(&ctr[0], 0, sizeof(ctr));
+ hcrypt_SetCtrIV((unsigned char *)&pki, ctx->salt, iv);
+
+ /* Decrypt message (same as encrypt for CTR mode) */
+ AES_ctr128_encrypt(in_data[0].payload, out_txt,
+ in_data[0].len, aes_key, iv, ctr, &blk_ofs);
+ out_len = in_data[0].len;
+ break;
+ }
+ case HCRYPT_CTX_MODE_AESECB:
+ {
+ int i;
+ int nb = in_data[0].len/AES_BLOCK_SIZE;
+ unsigned nbpad = ctx->msg_info->getPki(in_data[0].pfx, 0); //Patch
+ AES_KEY *aes_key = &aes_data->aes_key[hcryptCtx_GetKeyIndex(ctx)];
+
+ /* Decrypt message (same as encrypt for CTR mode) */
+ for (i=0; i 0) {
+ if (NULL == out_p) {
+ /* Decrypt in-place (in input buffer) */
+ memcpy(in_data[0].payload, out_txt, out_len);
+ in_data[0].len = out_len;
+ } else {
+ out_p[0] = out_txt;
+ out_len_p[0] = out_len;
+ *nbout_p = 1;
+ }
+ iret = 0;
+ } else {
+ if (NULL != nbout_p) *nbout_p = 0;
+ iret = -1;
+ }
+
+#if 0
+ { /* Debug decryption errors */
+ static int nberr = 0;
+
+ if (out_txt[0] != 0x47){
+ if ((++nberr == 1)
+ || ((nberr > 500) && (0 == ((((unsigned char *)&MSmsg->pki)[2] & 0x0F)|((unsigned char *)&MSmsg->pki)[3])))) {
+ HCRYPT_LOG(LOG_DEBUG, "keyindex=%d\n", hcryptCtx_GetKeyIndex(ctx));
+ HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
+ HCRYPT_PRINTKEY(ctx->salt, ctx->salt_len, "salt");
+ }
+ } else {
+ nberr = 0;
+ }
+ }
+#endif
+ return(iret);
+}
+
+
+static hcrypt_Cipher hcOpenSSL_AES_cipher;
+
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_AES(void)
+{
+ hcOpenSSL_AES_cipher.open = hcOpenSSL_AES_Open;
+ hcOpenSSL_AES_cipher.close = hcOpenSSL_AES_Close;
+ hcOpenSSL_AES_cipher.setkey = hcOpenSSL_AES_SetKey;
+ hcOpenSSL_AES_cipher.encrypt = hcOpenSSL_AES_Encrypt;
+ hcOpenSSL_AES_cipher.decrypt = hcOpenSSL_AES_Decrypt;
+
+ return((HaiCrypt_Cipher)&hcOpenSSL_AES_cipher);
+}
+
+#endif /* HAICRYPT_USE_OPENSSL_AES */
+
diff --git a/haicrypt/hc_openssl_evp_cbc.c b/haicrypt/hc_openssl_evp_cbc.c
new file mode 100644
index 000000000..f1cc11466
--- /dev/null
+++ b/haicrypt/hc_openssl_evp_cbc.c
@@ -0,0 +1,271 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2014-07-18 (jdube)
+ HaiCrypt initial implementation.
+*****************************************************************************/
+
+#include "hcrypt.h"
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
+
+#include
+#include
+
+#define HCRYPT_EVP_CBC_BLK_SZ AES_BLOCK_SIZE
+
+typedef struct tag_hcOpenSSL_EVP_CBC_data {
+ EVP_CIPHER_CTX evp_ctx[2];
+
+#define HCRYPT_OPENSSL_EVP_CBC_OUTMSGMAX 6
+ unsigned char * outbuf; /* output circle buffer */
+ size_t outbuf_ofs; /* write offset in circle buffer */
+ size_t outbuf_siz; /* circle buffer size */
+}hcOpenSSL_EVP_CBC_data;
+
+#if 0 //>>use engine
+#include
+
+static int hcOpenSSL_EnginesLoaded = 0;
+static ENGINE *hcOpenSSL_Engine = NULL;
+
+void hcOpenSSL_EnginesInit(void)
+{
+ if (!hcOpenSSL_EnginesLoaded) {
+#if 1
+ ENGINE *e = NULL;
+ ENGINE_load_cryptodev();
+
+ if (NULL == (e = ENGINE_by_id("cryptodev"))) {
+ HCRYPT_LOG(LOG_ERR, "%s", "Cryptodev not available\n");
+ } else if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "Cannot use cryptodev\n");
+ } else {
+ HCRYPT_LOG(LOG_DEBUG, "Using \"%s\" engine\n", ENGINE_get_id(e));
+ hcOpenSSL_Engine = e;
+ }
+ ENGINE_free(e);
+#else
+ HCRYPT_LOG(LOG_DEBUG, "%s", "Loading engines\n");
+ /* Load all bundled ENGINEs into memory and make them visible */
+ ENGINE_load_builtin_engines();
+ /* Register all of them for every algorithm they collectively implement */
+ ENGINE_register_all_complete();
+#endif
+ }
+ hcOpenSSL_EnginesLoaded++;
+}
+
+void hcOpenSSL_EnginesExit(void)
+{
+ if ((0 < hcOpenSSL_EnginesLoaded)
+ && (0 == --hcOpenSSL_EnginesLoaded)) {
+ ENGINE_cleanup();
+ HCRYPT_LOG(LOG_DEBUG, "%s", "Cleanup engines\n");
+ }
+}
+#else
+#define hcOpenSSL_Engine (NULL)
+#define hcOpenSSL_EnginesInit()
+#define hcOpenSSL_EnginesExit()
+#endif
+
+
+static unsigned char *hcOpenSSL_EVP_CBC_GetOutbuf(hcOpenSSL_EVP_CBC_data *evp_data, size_t hdr_len, size_t out_len)
+{
+ unsigned char *out_buf;
+ int nblks = (out_len + AES_BLOCK_SIZE -1) / AES_BLOCK_SIZE;
+ if ((hdr_len + (nblks * AES_BLOCK_SIZE)) > (evp_data->outbuf_siz - evp_data->outbuf_ofs)) {
+ /* Not enough room left, circle buffers */
+ evp_data->outbuf_ofs = 0;
+ }
+ out_buf = &evp_data->outbuf[evp_data->outbuf_ofs];
+ evp_data->outbuf_ofs += (hdr_len + (nblks * AES_BLOCK_SIZE));
+ return(out_buf);
+}
+
+static int hcOpenSSL_EVP_CBC_CipherData(EVP_CIPHER_CTX *evp_ctx,
+ unsigned char *in_ptr, size_t in_len, unsigned char *iv, unsigned char *out_ptr, size_t *out_len_ptr)
+{
+ int c_len = 0;
+ int f_len = 0;
+
+ /* allows reusing of 'e' for multiple encryption cycles */
+ if (!EVP_CipherInit_ex(evp_ctx, NULL, NULL, NULL, iv, -1)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher init failed\n");
+ } else if (!EVP_CipherUpdate(evp_ctx, out_ptr, &c_len, in_ptr, in_len)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher init failed\n");
+ } else if (!EVP_CipherFinal_ex(evp_ctx, &out_ptr[c_len], &f_len)) {
+ HCRYPT_LOG(LOG_ERR, "incomplete block (%zd/%d,%d)\n", in_len, c_len, f_len);
+ }
+ if (out_len_ptr) *out_len_ptr = c_len + f_len;
+ return(0);
+}
+
+
+static hcrypt_CipherData *hcOpenSSL_EVP_CBC_Open(size_t max_len)
+{
+ hcOpenSSL_EVP_CBC_data *evp_data;
+ unsigned char *membuf;
+ size_t padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
+ size_t memsiz = sizeof(*evp_data)
+ + (HCRYPT_OPENSSL_EVP_CBC_OUTMSGMAX * padded_len);
+
+
+ HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL EVP-CBC\n");
+
+ evp_data = malloc(memsiz);
+ if (NULL == evp_data) {
+ HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
+ return(NULL);
+ }
+ hcOpenSSL_EnginesInit();
+
+ membuf = (unsigned char *)evp_data;
+ membuf += sizeof(*evp_data);
+
+ evp_data->outbuf = membuf;
+// membuf += HCRYPT_OPENSSL_EVP_CBC_OUTMSGMAX * padded_len;
+ evp_data->outbuf_siz = HCRYPT_OPENSSL_EVP_CBC_OUTMSGMAX * padded_len;
+ evp_data->outbuf_ofs = 0;
+
+ EVP_CIPHER_CTX_init(&evp_data->evp_ctx[0]);
+// EVP_CIPHER_CTX_set_padding(&evp_data->evp_ctx[0], 0);
+
+ EVP_CIPHER_CTX_init(&evp_data->evp_ctx[1]);
+// EVP_CIPHER_CTX_set_padding(&evp_data->evp_ctx[1], 0);
+
+ return((hcrypt_CipherData *)evp_data);
+}
+
+static int hcOpenSSL_EVP_CBC_Close(hcrypt_CipherData *cipher_data)
+{
+ hcOpenSSL_EVP_CBC_data *evp_data = (hcOpenSSL_EVP_CBC_data *)cipher_data;
+
+ if (NULL != evp_data) {
+ EVP_CIPHER_CTX_cleanup(&evp_data->evp_ctx[0]);
+ EVP_CIPHER_CTX_cleanup(&evp_data->evp_ctx[1]);
+ free(evp_data);
+ }
+ hcOpenSSL_EnginesExit();
+ return(0);
+}
+
+static int hcOpenSSL_EVP_CBC_SetKey(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx, unsigned char *key, size_t key_len)
+{
+ hcOpenSSL_EVP_CBC_data *evp_data = (hcOpenSSL_EVP_CBC_data *)cipher_data;
+ EVP_CIPHER_CTX *evp_ctx = &evp_data->evp_ctx[hcryptCtx_GetKeyIndex(ctx)];
+ int enc = (ctx->flags & HCRYPT_CTX_F_ENCRYPT);
+ const EVP_CIPHER *cipher = NULL;
+
+ switch(key_len) {
+ case 128/8:
+ cipher = EVP_aes_128_cbc();
+ break;
+ case 192/8:
+ cipher = EVP_aes_192_cbc();
+ break;
+ case 256/8:
+ cipher = EVP_aes_256_cbc();
+ break;
+ default:
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid key length\n");
+ return(-1);
+ }
+ EVP_CipherInit_ex(evp_ctx, cipher, hcOpenSSL_Engine, key, NULL, enc);
+ return(0);
+}
+
+static int hcOpenSSL_EVP_CBC_Crypt(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx,
+ hcrypt_DataDesc *in_data, int nbin, void *out_p[], size_t out_len_p[], int *nbout_p)
+{
+ hcOpenSSL_EVP_CBC_data *evp_data = (hcOpenSSL_EVP_CBC_data *)cipher_data;
+ unsigned char iv[HCRYPT_EVP_CBC_BLK_SZ];
+ hcrypt_Pki pki;
+ unsigned char *out_msg;
+ size_t pfx_len;
+ size_t out_len;
+ int iret;
+
+ ASSERT(NULL != evp_data);
+ ASSERT(NULL != ctx);
+ ASSERT((NULL != in_data) && (1 == nbin));
+ ASSERT((NULL == out_p) || ((NULL != out_p) && (NULL != out_len_p) && (NULL != nbout_p)));
+
+ /* Room for prefix in output buffer only required for encryption */
+ pfx_len = ctx->flags & HCRYPT_CTX_F_ENCRYPT ? ctx->msg_info->pfx_len : 0;
+
+//>>set CBC eventually
+ if (HCRYPT_CTX_MODE_AESCTR != ctx->mode) {
+ HCRYPT_LOG(LOG_ERR, "invalid mode (%d) for cipher\n", ctx->mode);
+ return(-1);
+ }
+
+ /* Compute IV */
+ pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
+ hcrypt_SetCtrIV(&pki, ctx->salt, iv);
+
+ /* Reserve output buffer for cipher */
+ out_msg = hcOpenSSL_EVP_CBC_GetOutbuf(evp_data, pfx_len, in_data[0].len);
+
+ /* Encrypt */
+ iret = hcOpenSSL_EVP_CBC_CipherData(&evp_data->evp_ctx[hcryptCtx_GetKeyIndex(ctx)],
+ in_data[0].payload, in_data[0].len, iv, &out_msg[pfx_len], &out_len);
+ if (iret) {
+ HCRYPT_LOG(LOG_ERR, "%s", "CBC_CipherData failed\n");
+ return(iret);
+ }
+
+ /* Output cipher text */
+ if (out_len > 0) {
+ if (NULL == out_p) {
+ /* Copy output data back in input buffer */
+ memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
+ } else {
+ /* Copy header in output buffer if needed */
+ if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
+ out_p[0] = out_msg;
+ out_len_p[0] = pfx_len + out_len;
+ *nbout_p = 1;
+ }
+ iret = (int)out_len;
+ } else {
+ if (NULL != nbout_p) *nbout_p = 0;
+ iret = -1;
+ }
+ return(iret);
+}
+
+static hcrypt_Cipher hcOpenSSL_EVP_CBC_cipher;
+
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CBC(void)
+{
+ hcOpenSSL_EVP_CBC_cipher.open = hcOpenSSL_EVP_CBC_Open;
+ hcOpenSSL_EVP_CBC_cipher.close = hcOpenSSL_EVP_CBC_Close;
+ hcOpenSSL_EVP_CBC_cipher.setkey = hcOpenSSL_EVP_CBC_SetKey;
+ hcOpenSSL_EVP_CBC_cipher.encrypt = hcOpenSSL_EVP_CBC_Crypt;
+ hcOpenSSL_EVP_CBC_cipher.decrypt = hcOpenSSL_EVP_CBC_Crypt;
+
+ return((HaiCrypt_Cipher)&hcOpenSSL_EVP_CBC_cipher);
+}
+#endif /* HAICRYPT_USE_OPENSSL_EVP_CBC */
diff --git a/haicrypt/hc_openssl_evp_ctr.c b/haicrypt/hc_openssl_evp_ctr.c
new file mode 100644
index 000000000..1e1f76820
--- /dev/null
+++ b/haicrypt/hc_openssl_evp_ctr.c
@@ -0,0 +1,343 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+ 2014-07-15 (jdube)
+ Adaptation for EVP CTR mode
+*****************************************************************************/
+
+#include "hcrypt.h"
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP_CTR
+
+#include
+#include
+
+#define HCRYPT_EVP_CTR_BLK_SZ AES_BLOCK_SIZE
+
+typedef struct tag_hcOpenSSL_EVP_CTR_data {
+ EVP_CIPHER_CTX evp_ctx[2];
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+#define HCRYPT_EVP_CTR_STREAM_SZ 2048
+ unsigned char * ctr_stream;
+ size_t ctr_stream_len; /* Content size */
+ size_t ctr_stream_siz; /* Allocated length */
+#endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+
+#define HCRYPT_OPENSSL_EVP_CTR_OUTMSGMAX 6
+ unsigned char * outbuf; /* output circle buffer */
+ size_t outbuf_ofs; /* write offset in circle buffer */
+ size_t outbuf_siz; /* circle buffer size */
+}hcOpenSSL_EVP_CTR_data;
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+static int hcOpenSSL_EVP_CTR_SetCtrStream(hcOpenSSL_EVP_CTR_data *aes_data, hcrypt_Ctx *ctx, size_t len, unsigned char *iv)
+{
+ /* Counter stream:
+ * 0 1 2 3 4 5 nblk
+ * +---+---+---+---+---+---+---+---+
+ * |blk|blk|blk|blk|blk|blk|...|blk|
+ * +---+---+---+---+---+---+---+---+
+ */
+
+ /* IV (128-bit):
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | 0s | pki | ctr |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * XOR
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | nonce +
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * pki (32-bit): packet index
+ * ctr (16-bit): block counter
+ * nonce (112-bit): number used once (salt)
+ */
+ unsigned char ctr[HCRYPT_EVP_CTR_BLK_SZ];
+ unsigned nblk;
+
+ ASSERT(NULL != aes_data);
+ ASSERT(NULL != ctx);
+
+ memcpy(ctr, iv, HCRYPT_EVP_CTR_BLK_SZ);
+
+ nblk = (len + (HCRYPT_EVP_CTR_BLK_SZ-1))/HCRYPT_EVP_CTR_BLK_SZ;
+ if ((nblk * HCRYPT_EVP_CTR_BLK_SZ) <= aes_data->ctr_stream_siz) {
+ unsigned blk;
+ unsigned char *csp = &aes_data->ctr_stream[0];
+
+ for(blk = 0; blk < nblk; blk++) {
+ memcpy(csp, ctr, HCRYPT_EVP_CTR_BLK_SZ);
+ csp += HCRYPT_EVP_CTR_BLK_SZ;
+ if (0 == ++(ctr[HCRYPT_EVP_CTR_BLK_SZ-1])) ++(ctr[HCRYPT_EVP_CTR_BLK_SZ-2]);
+ }
+ aes_data->ctr_stream_len = nblk * HCRYPT_EVP_CTR_BLK_SZ;
+ } else {
+ HCRYPT_LOG(LOG_ERR, "packet too long(%zd)\n", len);
+ return(-1);
+ }
+ return(0);
+}
+#endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+
+static unsigned char *hcOpenSSL_EVP_CTR_GetOutbuf(hcOpenSSL_EVP_CTR_data *evp_data, size_t hdr_len, size_t out_len)
+{
+ unsigned char *out_buf;
+
+ if ((hdr_len + out_len) > (evp_data->outbuf_siz - evp_data->outbuf_ofs)) {
+ /* Not enough room left, circle buffers */
+ evp_data->outbuf_ofs = 0;
+ }
+ out_buf = &evp_data->outbuf[evp_data->outbuf_ofs];
+ evp_data->outbuf_ofs += (hdr_len + out_len);
+ return(out_buf);
+}
+
+static int hcOpenSSL_EVP_CTR_CipherData(EVP_CIPHER_CTX *evp_ctx,
+ unsigned char *in_ptr, size_t in_len, unsigned char *iv, unsigned char *out_ptr, size_t *out_len_ptr)
+{
+ int c_len, f_len;
+
+ /* allows reusing of 'e' for multiple encryption cycles */
+ EVP_CipherInit_ex(evp_ctx, NULL, NULL, NULL, iv, -1);
+ EVP_CIPHER_CTX_set_padding(evp_ctx, 0);
+
+ /* update ciphertext, c_len is filled with the length of ciphertext generated,
+ * cryptoPtr->cipher_in_len is the size of plain/cipher text in bytes
+ */
+ EVP_CipherUpdate(evp_ctx, out_ptr, &c_len, in_ptr, in_len);
+
+ /* update ciphertext with the final remaining bytes */
+ /* Useless with pre-padding */
+ f_len = 0;
+ if (0 == EVP_CipherFinal_ex(evp_ctx, &out_ptr[c_len], &f_len)) {
+ HCRYPT_LOG(LOG_ERR, "incomplete block (%d/%zd)\n", c_len, in_len);
+ }
+ if (out_len_ptr) *out_len_ptr = c_len + f_len;
+ return(0);
+}
+
+
+static hcrypt_CipherData *hcOpenSSL_EVP_CTR_Open(size_t max_len)
+{
+ hcOpenSSL_EVP_CTR_data *evp_data;
+ unsigned char *membuf;
+ size_t padded_len = hcryptMsg_PaddedLen(max_len, 128/8);
+ size_t memsiz = sizeof(*evp_data)
+#ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ + HCRYPT_EVP_CTR_STREAM_SZ
+#endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ + (HCRYPT_OPENSSL_EVP_CTR_OUTMSGMAX * padded_len);
+
+ HCRYPT_LOG(LOG_DEBUG, "%s", "Using OpenSSL EVP-CTR\n");
+
+ evp_data = malloc(memsiz);
+ if (NULL == evp_data) {
+ HCRYPT_LOG(LOG_ERR, "malloc(%zd) failed\n", memsiz);
+ return(NULL);
+ }
+ membuf = (unsigned char *)evp_data;
+ membuf += sizeof(*evp_data);
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ evp_data->ctr_stream = membuf;
+ membuf += HCRYPT_EVP_CTR_STREAM_SZ;
+ evp_data->ctr_stream_siz = HCRYPT_EVP_CTR_STREAM_SZ;
+ evp_data->ctr_stream_len = 0;
+#endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+
+ evp_data->outbuf = membuf;
+ evp_data->outbuf_siz = HCRYPT_OPENSSL_EVP_CTR_OUTMSGMAX * padded_len;
+ evp_data->outbuf_ofs = 0;
+
+ EVP_CIPHER_CTX_init(&evp_data->evp_ctx[0]);
+ EVP_CIPHER_CTX_set_padding(&evp_data->evp_ctx[0], 0);
+
+ EVP_CIPHER_CTX_init(&evp_data->evp_ctx[1]);
+ EVP_CIPHER_CTX_set_padding(&evp_data->evp_ctx[1], 0);
+
+ return((hcrypt_CipherData *)evp_data);
+}
+
+static int hcOpenSSL_EVP_CTR_Close(hcrypt_CipherData *cipher_data)
+{
+ hcOpenSSL_EVP_CTR_data *evp_data = (hcOpenSSL_EVP_CTR_data *)cipher_data;
+
+ if (NULL != evp_data) {
+ EVP_CIPHER_CTX_cleanup(&evp_data->evp_ctx[0]);
+ EVP_CIPHER_CTX_cleanup(&evp_data->evp_ctx[1]);
+ free(evp_data);
+ }
+ return(0);
+}
+
+static int hcOpenSSL_EVP_CTR_SetKey(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx, unsigned char *key, size_t key_len)
+{
+ hcOpenSSL_EVP_CTR_data *evp_data = (hcOpenSSL_EVP_CTR_data *)cipher_data;
+ EVP_CIPHER_CTX *evp_ctx = &evp_data->evp_ctx[hcryptCtx_GetKeyIndex(ctx)];
+ int enc = ((ctx->flags & HCRYPT_CTX_F_ENCRYPT) || (ctx->mode == HCRYPT_CTX_MODE_AESCTR));
+ const EVP_CIPHER *cipher = NULL;
+
+ switch(key_len) {
+#ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ case 128/8:
+ cipher = EVP_aes_128_ecb();
+ break;
+ case 192/8:
+ cipher = EVP_aes_192_ecb();
+ break;
+ case 256/8:
+ cipher = EVP_aes_256_ecb();
+ break;
+#else /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ case 128/8:
+ cipher = EVP_aes_128_ctr();
+ break;
+ case 192/8:
+ cipher = EVP_aes_192_ctr();
+ break;
+ case 256/8:
+ cipher = EVP_aes_256_ctr();
+ break;
+#endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ default:
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid key length\n");
+ return(-1);
+ }
+
+ EVP_CipherInit_ex(evp_ctx, cipher, NULL, key, NULL, enc);
+ return(0);
+}
+
+static int hcOpenSSL_EVP_CTR_Crypt(hcrypt_CipherData *cipher_data, hcrypt_Ctx *ctx,
+ hcrypt_DataDesc *in_data, int nbin, void *out_p[], size_t out_len_p[], int *nbout_p)
+{
+ hcOpenSSL_EVP_CTR_data *evp_data = (hcOpenSSL_EVP_CTR_data *)cipher_data;
+ unsigned char iv[HCRYPT_EVP_CTR_BLK_SZ];
+ hcrypt_Pki pki;
+ unsigned char *out_msg;
+ size_t pfx_len;
+ size_t out_len;
+ int iret;
+
+ ASSERT(NULL != evp_data);
+ ASSERT(NULL != ctx);
+ ASSERT((NULL != in_data) && (1 == nbin));
+
+ /* Room for prefix in output buffer only required for encryption */
+ pfx_len = ctx->flags & HCRYPT_CTX_F_ENCRYPT ? ctx->msg_info->pfx_len : 0;
+
+ if (HCRYPT_CTX_MODE_AESCTR != ctx->mode) {
+ HCRYPT_LOG(LOG_ERR, "invalid mode (%d) for cipher\n", ctx->mode);
+ return(-1);
+ }
+
+ /* Compute IV */
+ pki = hcryptMsg_GetPki(ctx->msg_info, in_data[0].pfx, 1);
+ hcrypt_SetCtrIV(&pki, ctx->salt, iv);
+
+ #ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ /* Create CtrStream. May be longer than in_len (next cipher block size boundary) */
+ iret = hcOpenSSL_EVP_CTR_SetCtrStream(evp_data, ctx, in_data[0].len, iv);
+ if (iret) {
+ return(iret);
+ }
+ /* Reserve output buffer for cipher */
+ out_msg = hcOpenSSL_EVP_CTR_GetOutbuf(evp_data, pfx_len, evp_data->ctr_stream_len);
+
+ /* Create KeyStream (encrypt CtrStream) */
+ iret = hcOpenSSL_EVP_CTR_CipherData(&evp_data->evp_ctx[hcryptCtx_GetKeyIndex(ctx)],
+ evp_data->ctr_stream, evp_data->ctr_stream_len, NULL,
+ &out_msg[pfx_len], &out_len);
+ if (iret) {
+ HCRYPT_LOG(LOG_ERR, "%s", "CRYPTOEVP_CipherData failed\n");
+ return(iret);
+ }
+
+ #else /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ /* Reserve output buffer for cipher */
+ out_msg = hcOpenSSL_EVP_CTR_GetOutbuf(evp_data, pfx_len, in_data[0].len);
+
+ /* Encrypt */
+ iret = hcOpenSSL_EVP_CTR_CipherData(&evp_data->evp_ctx[hcryptCtx_GetKeyIndex(ctx)],
+ in_data[0].payload, in_data[0].len, iv,
+ &out_msg[pfx_len], &out_len);
+ if (iret) {
+ HCRYPT_LOG(LOG_ERR, "%s", "CTR_CipherData failed\n");
+ return(iret);
+ }
+ #endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+
+ /* Output clear or cipher text */
+ if (out_len > 0) {
+ if (NULL == out_p) {
+ #ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ /* XOR KeyStream with input text directly in input buffer */
+ hcrypt_XorStream(in_data[0].payload, &out_msg[pfx_len], out_len);
+ #else /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ /* Copy output data back in input buffer */
+ memcpy(in_data[0].payload, &out_msg[pfx_len], out_len);
+ #endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ } else {
+ /* Copy header in output buffer if needed */
+ if (pfx_len > 0) memcpy(out_msg, in_data[0].pfx, pfx_len);
+ #ifdef HAICRYPT_USE_OPENSSL_EVP_ECB4CTR
+ hcrypt_XorStream(&out_msg[pfx_len], in_data[0].payload, out_len);
+ #endif /* HAICRYPT_USE_OPENSSL_EVP_ECB4CTR */
+ out_p[0] = out_msg;
+ out_len_p[0] = pfx_len + out_len;
+ *nbout_p = 1;
+ }
+ iret = 0;
+ } else {
+ if (NULL != nbout_p) *nbout_p = 0;
+ iret = -1;
+ }
+ return(iret);
+}
+
+static hcrypt_Cipher hcOpenSSL_EVP_CTR_cipher;
+
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CTR(void)
+{
+ hcOpenSSL_EVP_CTR_cipher.open = hcOpenSSL_EVP_CTR_Open;
+ hcOpenSSL_EVP_CTR_cipher.close = hcOpenSSL_EVP_CTR_Close;
+ hcOpenSSL_EVP_CTR_cipher.setkey = hcOpenSSL_EVP_CTR_SetKey;
+ hcOpenSSL_EVP_CTR_cipher.encrypt = hcOpenSSL_EVP_CTR_Crypt; // Counter Mode encrypt and
+ hcOpenSSL_EVP_CTR_cipher.decrypt = hcOpenSSL_EVP_CTR_Crypt; // ...decrypt are the same
+
+ return((HaiCrypt_Cipher)&hcOpenSSL_EVP_CTR_cipher);
+}
+
+/* Backward compatible call when only CTR was available */
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP(void)
+{
+ return(HaiCryptCipher_OpenSSL_EVP_CTR());
+}
+
+#endif /* HAICRYPT_USE_OPENSSL_EVP_CTR */
diff --git a/haicrypt/hcrypt.c b/haicrypt/hcrypt.c
new file mode 100644
index 000000000..45698e0cb
--- /dev/null
+++ b/haicrypt/hcrypt.c
@@ -0,0 +1,204 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* snprintf */
+#include /* NULL, malloc, free */
+#include /* memcpy, memset */
+#ifdef WIN32
+ #include
+ #include
+#else
+ #include /* timerclear */
+#endif
+
+#include "hcrypt.h"
+
+int HaiCrypt_Create(HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc)
+{
+ hcrypt_Session *crypto;
+ hcrypt_Cipher *cipher;
+ unsigned char *mem_buf;
+ size_t mem_siz, inbuf_siz;
+ int tx;
+
+ *phhc = NULL;
+
+ ASSERT(NULL != cfg);
+
+ HCRYPT_LOG_INIT();
+ //Test log
+ HCRYPT_LOG(LOG_INFO, "creating crypto context(flags=0x%x)\n", cfg->flags);
+
+ if (!(HAICRYPT_CFG_F_CRYPTO & cfg->flags)) {
+ HCRYPT_LOG(LOG_INFO, "no supported flags set (0x%x)\n", cfg->flags);
+ return(-1);
+ } else
+ if ((16 != cfg->key_len) /* SEK length */
+ && (24 != cfg->key_len)
+ && (32 != cfg->key_len)) {
+ HCRYPT_LOG(LOG_ERR, "invalid key length (%zd)\n", cfg->key_len);
+ return(-1);
+ } else
+ if ((HAICRYPT_SECTYP_PASSPHRASE == cfg->secret.typ)
+ && ((0 == cfg->secret.len) || (sizeof(cfg->secret.str) < cfg->secret.len))) { /* KEK length */
+ HCRYPT_LOG(LOG_ERR, "invalid secret passphrase length (%zd)\n", cfg->secret.len);
+ return(-1);
+ } else
+ if ((HAICRYPT_SECTYP_PRESHARED == cfg->secret.typ)
+ && (16 != cfg->key_len) /* SEK length */
+ && (24 != cfg->key_len)
+ && (32 != cfg->key_len)) {
+ HCRYPT_LOG(LOG_ERR, "invalid pre-shared secret length (%zd)\n", cfg->secret.len);
+ return(-1);
+ } else
+ if ((HAICRYPT_SECTYP_PRESHARED == cfg->secret.typ)
+ && (cfg->key_len > cfg->secret.len)) {
+ HCRYPT_LOG(LOG_ERR, "preshared secret length (%zd) smaller than key length (%zd)\n",
+ cfg->secret.len, cfg->key_len);
+ return(-1);
+ } else
+ if (NULL == cfg->cipher) {
+ HCRYPT_LOG(LOG_ERR, "%s\n", "no cipher specified");
+ return(-1);
+ } else
+ if (0 == cfg->data_max_len) {
+ HCRYPT_LOG(LOG_ERR, "%s\n", "no data_max_len specified");
+ return(-1);
+ }
+
+ cipher = (hcrypt_Cipher *)cfg->cipher;
+ tx = HAICRYPT_CFG_F_TX & cfg->flags;
+ HCRYPT_PRINTKEY(cfg->secret.str, cfg->secret.len, "cfgkey");
+
+ /*
+ * If cipher has no special input buffer alignment requirement,
+ * handle it in the crypto session.
+ */
+ inbuf_siz = 0;
+ if (NULL == cipher->getinbuf) {
+ inbuf_siz = hcryptMsg_PaddedLen(cfg->data_max_len, 128/8);
+ }
+
+ /* Allocate crypto session control struct */
+ mem_siz = sizeof(hcrypt_Session) // structure
+ + inbuf_siz;
+
+ crypto = malloc(mem_siz);
+ if (NULL == crypto){
+ HCRYPT_LOG(LOG_ERR, "%s\n", "malloc failed");
+ return(-1);
+ }
+ mem_buf = (unsigned char *)crypto;
+ mem_buf += sizeof(*crypto);
+ memset(crypto, 0, sizeof(*crypto));
+
+ if (inbuf_siz) {
+ crypto->inbuf = mem_buf;
+ crypto->inbuf_siz = inbuf_siz;
+ mem_buf += inbuf_siz;
+ }
+
+ crypto->cipher = cfg->cipher;
+
+ /* Setup transport packet info */
+ switch (cfg->xport) {
+ case HAICRYPT_XPT_STANDALONE:
+ crypto->se = HCRYPT_SE_TSUDP;
+ crypto->msg_info = hcryptMsg_STA_MsgInfo();
+ break;
+ case HAICRYPT_XPT_SRT:
+ crypto->se = HCRYPT_SE_TSSRT;
+ crypto->msg_info = hcryptMsg_SRT_MsgInfo();
+ break;
+ default:
+ HCRYPT_LOG(LOG_ERR, "invalid xport: %d\n", cfg->xport);
+ free(crypto);
+ return(-1);
+ }
+
+ timerclear(&crypto->km.tx_last);
+ crypto->km.tx_period.tv_sec = cfg->km_tx_period_ms / 1000;
+ crypto->km.tx_period.tv_usec = (cfg->km_tx_period_ms % 1000) * 1000;
+
+ crypto->km.refresh_rate = cfg->km_refresh_rate_pkt;
+ crypto->km.pre_announce = cfg->km_pre_announce_pkt;
+
+ /* Indentify each context */
+ crypto->ctx_pair[0].flags = HCRYPT_MSG_F_eSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
+ crypto->ctx_pair[1].flags = HCRYPT_MSG_F_oSEK | (tx ? HCRYPT_CTX_F_ENCRYPT : 0);
+ /* Point to each other */
+ crypto->ctx_pair[0].alt = &crypto->ctx_pair[1];
+ crypto->ctx_pair[1].alt = &crypto->ctx_pair[0];
+
+ crypto->cipher_data = crypto->cipher->open(cfg->data_max_len);
+ if (NULL == crypto->cipher_data) {
+ free(crypto);
+ return(-1);
+ }
+ if (tx) { /* Encoder */
+ /* Configure initial context */
+ if (hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[0], cfg)
+ || hcryptCtx_Tx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
+ free(crypto);
+ return(-1);
+ }
+ /* Generate keys for first (default) context */
+ if (hcryptCtx_Tx_Rekey(crypto, &crypto->ctx_pair[0])) {
+ free(crypto);
+ return(-1);
+ }
+ crypto->ctx = &crypto->ctx_pair[0];
+ crypto->ctx->flags |= (HCRYPT_CTX_F_ANNOUNCE | HCRYPT_CTX_F_TTSEND);
+ crypto->ctx->status = HCRYPT_CTX_S_ACTIVE;
+ } else { /* Decoder */
+ /* Configure contexts */
+ if (hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[0], cfg)
+ || hcryptCtx_Rx_Init(crypto, &crypto->ctx_pair[1], cfg)) {
+ free(crypto);
+ return(-1);
+ }
+ }
+
+ *phhc = (void *)crypto;
+ return(0);
+}
+
+int HaiCrypt_Close(HaiCrypt_Handle hhc)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ int rc = -1;
+
+ if (crypto) {
+ if (crypto->cipher && crypto->cipher->close) crypto->cipher->close(crypto->cipher_data);
+ free(crypto);
+ rc = 0;
+ }
+ HCRYPT_LOG_EXIT();
+ return rc;
+}
diff --git a/haicrypt/hcrypt.h b/haicrypt/hcrypt.h
new file mode 100644
index 000000000..28dfbe0cd
--- /dev/null
+++ b/haicrypt/hcrypt.h
@@ -0,0 +1,220 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+ 2014-03-26 (jsantiago)
+ OS-X Build.
+ 2014-03-27 (jdube)
+ Remove dependency on internal Crypto API.
+ 2016-07-22 (jsantiago)
+ MINGW-W64 Build.
+*****************************************************************************/
+
+#ifndef HCRYPT_H
+#define HCRYPT_H
+
+#include
+
+#ifdef WIN32
+ #include
+ #include
+ #if defined(_MSC_VER)
+ #pragma warning(disable:4267)
+ #pragma warning(disable:4018)
+ #endif
+#else
+ #include
+#endif
+
+#include "haicrypt.h"
+#include "hcrypt_msg.h"
+#include "hcrypt_ctx.h"
+
+//#define HCRYPT_DEV 1 /* Development: should not be defined in committed code */
+
+#ifdef HAICRYPT_SUPPORT_CRYPTO_API
+/* See CRYPTOFEC_OBJECT in session structure */
+#define CRYPTO_API_SERVER 1 /* Enable handler's structures */
+#include "crypto_api.h"
+#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
+
+typedef struct {
+#ifdef HAICRYPT_SUPPORT_CRYPTO_API
+ /*
+ * Resv matches internal upper layer handle (crypto_api)
+ * They are not used in HaiCrypt.
+ * This make 3 layers using the same handle.
+ * To get rid of this dependency for a portable HaiCrypt,
+ * revise caller (crypto_hc.c) to allocate its own buffer.
+ */
+ CRYPTOFEC_OBJECT resv; /* See above comment */
+#endif /* HAICRYPT_SUPPORT_CRYPTO_API */
+
+ hcrypt_Ctx ctx_pair[2]; /* Even(0)/Odd(1) crypto contexts */
+ hcrypt_Ctx * ctx; /* Current context */
+
+ hcrypt_Cipher * cipher;
+ hcrypt_CipherData * cipher_data;
+
+ unsigned char * inbuf; /* allocated if cipher has no getinbuf() func */
+ size_t inbuf_siz;
+
+ int se; /* Stream Encapsulation (HCRYPT_SE_xxx) */
+ hcrypt_MsgInfo * msg_info;
+
+ struct {
+ struct timeval tx_period; /* Keying Material tx period (milliseconds) */
+ struct timeval tx_last; /* Keying Material last tx time */
+ unsigned int refresh_rate; /* SEK use period */
+ unsigned int pre_announce; /* Pre/Post next/old SEK announce */
+ }km;
+} hcrypt_Session;
+
+
+#define HCRYPT_LOG_INIT()
+#define HCRYPT_LOG_EXIT()
+#define HCRYPT_LOG(lvl, fmt, ...)
+
+#ifdef HCRYPT_DEV
+#define HCRYPT_PRINTKEY(key, len, tag) HCRYPT_LOG(LOG_DEBUG, \
+ "%s[%d]=0x%02x%02x..%02x%02x\n", tag, len, \
+ (key)[0], (key)[1], (key)[(len)-2], (key)[(len)-1])
+#else /* HCRYPT_DEV */
+#define HCRYPT_PRINTKEY(key,len,tag)
+#endif /* HCRYPT_DEV */
+
+#ifndef ASSERT
+#include
+#define ASSERT(c) assert(c)
+#endif
+
+#ifdef HAICRYPT_USE_OPENSSL_AES
+#include /* OPENSSL_VERSION_NUMBER */
+#include /* PKCS5_ */
+#include /* RAND_bytes */
+#include /* AES_ */
+
+#define hcrypt_Prng(rn, len) (RAND_bytes(rn, len) <= 0 ? -1 : 0)
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090808fL) //0.9.8h
+ /*
+ * AES_wrap_key()/AES_unwrap_key() introduced in openssl 0.9.8h
+ * Use internal implementation (in hc_openssl_aes.c) for earlier versions
+ */
+int AES_wrap_key(AES_KEY *key, const unsigned char *iv, unsigned char *out,
+ const unsigned char *in, unsigned int inlen);
+int AES_unwrap_key(AES_KEY *key, const unsigned char *iv, unsigned char *out,
+ const unsigned char *in, unsigned int inlen);
+#endif /* OPENSSL_VERSION_NUMBER */
+
+#define hcrypt_WrapKey(kek, wrap, key, keylen) (((int)(keylen + HAICRYPT_WRAPKEY_SIGN_SZ) \
+ == AES_wrap_key(kek, NULL, wrap, key, keylen)) ? 0 : -1)
+#define hcrypt_UnwrapKey(kek, key, wrap, wraplen) (((int)(wraplen - HAICRYPT_WRAPKEY_SIGN_SZ) \
+ == AES_unwrap_key(kek, NULL, key, wrap, wraplen)) ? 0 : -1)
+
+#else /* HAICRYPT_USE_OPENSSL_AES */
+#error No Prng and key wrapper defined
+
+#endif /* HAICRYPT_USE_OPENSSL_AES */
+
+
+#ifdef HAICRYPT_USE_OPENSSL_EVP
+#include // OPENSSL_VERSION_NUMBER
+
+#define HAICRYPT_USE_OPENSSL_EVP_CTR 1
+ /*
+ * CTR mode is the default mode for HaiCrypt (standalone and SRT)
+ */
+#ifdef HAICRYPT_USE_OPENSSL_EVP_CTR
+#if (OPENSSL_VERSION_NUMBER < 0x10001000L)
+ /*
+ * CTR mode for EVP API introduced in openssl 1.0.1
+ * Implement it using ECB mode for earlier versions
+ */
+#define HAICRYPT_USE_OPENSSL_EVP_ECB4CTR 1
+#endif
+ HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CTR(void);
+#endif
+
+//undef HAICRYPT_USE_OPENSSL_EVP_CBC 1 /* CBC mode (for crypto engine tests) */
+ /*
+ * CBC mode for crypto engine tests
+ * Default CTR mode not supported on Linux cryptodev (API to hardware crypto engines)
+ * Not officially support nor interoperable with any haicrypt peer
+ */
+#ifdef HAICRYPT_USE_OPENSSL_EVP_CBC
+ HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CBC(void);
+#endif /* HAICRYPT_USE_OPENSSL_EVP_CBC */
+
+#endif /* HAICRYPT_USE_OPENSSL_EVP */
+
+
+/* HaiCrypt-TP CTR mode IV (128-bit):
+ * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | 0s | pki | ctr |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * XOR
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | nonce +
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * pki (32-bit): packet index
+ * ctr (16-bit): block counter
+ * nonce (112-bit): number used once (salt)
+ */
+#define hcrypt_SetCtrIV(pki, nonce, iv) do { \
+ memset(&(iv)[0], 0, 128/8); \
+ memcpy(&(iv)[10], (pki), HCRYPT_PKI_SZ); \
+ hcrypt_XorStream(&(iv)[0], (nonce), 112/8); \
+ } while(0)
+
+#define hcrypt_XorStream(dst, strm, len) do { \
+ int __XORSTREAMi; \
+ for (__XORSTREAMi = 0 \
+ ;__XORSTREAMi < (int)(len) \
+ ;__XORSTREAMi += 1) { \
+ (dst)[__XORSTREAMi] ^= (strm)[__XORSTREAMi]; \
+ } \
+ } while(0)
+
+
+int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Secret *secret);
+int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
+
+int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Cfg *cfg);
+int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx);
+int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto);
+int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto);
+int hcryptCtx_Tx_Switch(hcrypt_Session *crypto);
+int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto);
+int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek);
+int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto);
+int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto, void *out_p[], size_t out_len_p[], int maxout);
+
+int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Cfg *cfg);
+int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *msg, size_t msg_len);
+
+#endif /* HCRYPT_H */
diff --git a/haicrypt/hcrypt_ctx_rx.c b/haicrypt/hcrypt_ctx_rx.c
new file mode 100644
index 000000000..8546700a7
--- /dev/null
+++ b/haicrypt/hcrypt_ctx_rx.c
@@ -0,0 +1,207 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* memcpy */
+#include "hcrypt.h"
+
+int hcryptCtx_Rx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Cfg *cfg)
+{
+ ctx->mode = HCRYPT_CTX_MODE_AESCTR;
+ ctx->status = HCRYPT_CTX_S_INIT;
+
+ ctx->msg_info = crypto->msg_info;
+
+ if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
+ return(-1);
+ }
+ return(0);
+}
+
+int hcryptCtx_Rx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *sek, size_t sek_len)
+{
+ if (crypto->cipher->setkey(crypto->cipher_data, ctx, sek, sek_len)) {
+ HCRYPT_LOG(LOG_ERR, "cipher setkey[%d](sek) failed\n", hcryptCtx_GetKeyIndex(ctx));
+ return(-1);
+ }
+ memcpy(ctx->sek, sek, sek_len);
+ ctx->sek_len = sek_len;
+
+ HCRYPT_LOG(LOG_INFO, "updated context[%d]\n", hcryptCtx_GetKeyIndex(ctx));
+ HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
+ ctx->status = HCRYPT_CTX_S_KEYED;
+ return(0);
+}
+
+/* Parse Keying Material message */
+int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t msg_len)
+{
+ size_t sek_len, salt_len;
+ unsigned char seks[HAICRYPT_KEY_MAX_SZ * 2];
+ int sek_cnt;
+ size_t kek_len = 0;
+ hcrypt_Ctx *ctx;
+ int do_pbkdf = 0;
+
+ if (NULL == crypto) {
+ HCRYPT_LOG(LOG_INFO, "%s", "Invalid params\n");
+ return(-1);
+ }
+
+ /* Validate message content */
+ {
+ if (msg_len <= HCRYPT_MSG_KM_OFS_SALT) {
+ HCRYPT_LOG(LOG_WARNING, "KMmsg length too small (%zd)\n", msg_len);
+ return(-1);
+ }
+ salt_len = hcryptMsg_KM_GetSaltLen(km_msg);
+ sek_len = hcryptMsg_KM_GetSekLen(km_msg);
+
+ if ((salt_len > HAICRYPT_SALT_SZ)
+ || (sek_len > HAICRYPT_KEY_MAX_SZ)) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported salt/key length\n");
+ return(-1);
+ }
+ if ((16 != sek_len)
+ && (24 != sek_len)
+ && (32 != sek_len)) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported key length\n");
+ return(-1);
+ }
+ if (hcryptMsg_KM_HasBothSek(km_msg)) {
+ sek_cnt = 2;
+ } else {
+ sek_cnt = 1;
+ }
+ if (msg_len != (HCRYPT_MSG_KM_OFS_SALT + salt_len + (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
+ HCRYPT_LOG(LOG_WARNING, "KMmsg length inconsistent (%zd,%zd,%zd)\n",
+ salt_len, sek_len, msg_len);
+ return(-1);
+ }
+
+ /* Check options support */
+ if ((HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER])
+ || (HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH])) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported option\n");
+ return(-1);
+ }
+
+ if (crypto->se != km_msg[HCRYPT_MSG_KM_OFS_SE]) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid SE\n");
+ return(-1);
+ }
+
+ /* Check KEKI here and pick right key */
+ //>>todo
+ /*
+ * We support no key exchange,
+ * KEK is preshared or derived from a passphrase
+ */
+ }
+
+ /* Pick the context updated by this KMmsg */
+ if (hcryptMsg_KM_HasBothSek(km_msg) && (NULL != crypto->ctx)) {
+ ctx = crypto->ctx->alt; /* 2 SEK KM, start with inactive ctx */
+ } else {
+ ctx = &crypto->ctx_pair[hcryptMsg_KM_GetKeyIndex(km_msg)];
+ }
+ if (NULL == ctx) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg invalid flags (no SEK)\n");
+ return(-1);
+ }
+
+ /* Check Salt and get if new */
+ if ((salt_len != ctx->salt_len)
+ || (0 != memcmp(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len))) {
+ /* Salt changed (or 1st KMmsg received) */
+ memcpy(ctx->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
+ ctx->salt_len = salt_len;
+ do_pbkdf = 1; /* Impact on password derived kek */
+ }
+
+ /* Check SEK length and get if new */
+ if (sek_len != ctx->sek_len) {
+ /* Key length changed or 1st KMmsg received */
+ ctx->sek_len = sek_len;
+ do_pbkdf = 1; /* Impact on password derived kek */
+ }
+
+ /*
+ * Regenerate KEK if it is password derived
+ * and Salt or SEK length changed
+ */
+ if (ctx->cfg.pwd_len && do_pbkdf) {
+ if (hcryptCtx_GenSecret(crypto, ctx)) {
+ return(-1);
+ }
+ ctx->status = HCRYPT_CTX_S_SARDY;
+ kek_len = sek_len; /* KEK changed */
+ }
+
+ /* Unwrap SEK(s) and set in context */
+ if (0 > hcrypt_UnwrapKey(&ctx->aes_kek, seks,
+ &km_msg[HCRYPT_MSG_KM_OFS_SALT + salt_len],
+ (sek_cnt * sek_len) + HAICRYPT_WRAPKEY_SIGN_SZ)) {
+ HCRYPT_LOG(LOG_WARNING, "%s", "unwrap key failed\n");
+ return(-2); //Report unmatched shared secret
+ }
+
+ /*
+ * First SEK in KMmsg is eSEK if both SEK present
+ */
+ hcryptCtx_Rx_Rekey(crypto, ctx,
+ ((2 == sek_cnt) && (ctx->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
+ sek_len);
+
+ /*
+ * Refresh KMmsg cache to detect Keying Material changes
+ */
+ ctx->KMmsg_len = msg_len;
+ memcpy(ctx->KMmsg_cache, km_msg, msg_len);
+
+ /* update other (alternate) context if both SEK provided */
+ if (2 == sek_cnt) {
+ hcrypt_Ctx *alt = ctx->alt;
+
+ memcpy(alt->salt, &km_msg[HCRYPT_MSG_KM_OFS_SALT], salt_len);
+ alt->salt_len = salt_len;
+
+ if (kek_len) { /* New or changed KEK */
+ memcpy(&alt->aes_kek, &ctx->aes_kek, sizeof(alt->aes_kek));
+ alt->status = HCRYPT_CTX_S_SARDY;
+ }
+
+ hcryptCtx_Rx_Rekey(crypto, alt,
+ ((2 == sek_cnt) && (alt->flags & HCRYPT_MSG_F_oSEK)) ? &seks[sek_len] : &seks[0],
+ sek_len);
+
+ alt->KMmsg_len = msg_len;
+ memcpy(alt->KMmsg_cache, km_msg, msg_len);
+ }
+ return(0);
+}
diff --git a/haicrypt/hcrypt_ctx_tx.c b/haicrypt/hcrypt_ctx_tx.c
new file mode 100644
index 000000000..879ab3295
--- /dev/null
+++ b/haicrypt/hcrypt_ctx_tx.c
@@ -0,0 +1,360 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* memcpy */
+#ifdef WIN32
+ #include
+ #include
+ #include
+#else
+ #include
+#endif
+#include "hcrypt.h"
+
+int hcryptCtx_Tx_Init(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Cfg *cfg)
+{
+ ctx->cfg.key_len = cfg->key_len;
+
+ ctx->mode = HCRYPT_CTX_MODE_AESCTR;
+ ctx->status = HCRYPT_CTX_S_INIT;
+
+ ctx->msg_info = crypto->msg_info;
+
+ if (hcryptCtx_SetSecret(crypto, ctx, &cfg->secret)) {
+ return(-1);
+ }
+ return(0);
+}
+
+int hcryptCtx_Tx_Rekey(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
+{
+ int iret;
+
+ ASSERT(HCRYPT_CTX_S_SARDY <= ctx->status);
+
+ /* Generate Salt */
+ ctx->salt_len = HAICRYPT_SALT_SZ;
+ if (0 > (iret = hcrypt_Prng(ctx->salt, ctx->salt_len))) {
+ HCRYPT_LOG(LOG_ERR, "PRNG(salt[%zd]) failed\n", ctx->salt_len);
+ return(iret);
+ }
+
+ /* Generate SEK */
+ ctx->sek_len = ctx->cfg.key_len;
+ if (0 > (iret = hcrypt_Prng(ctx->sek, ctx->sek_len))) {
+ HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", ctx->sek_len);
+ return(iret);
+ }
+
+ /* Set SEK in cipher */
+ if (crypto->cipher->setkey(crypto->cipher_data, ctx, ctx->sek, ctx->sek_len)) {
+ HCRYPT_LOG(LOG_ERR, "cipher setkey(sek[%zd]) failed\n", ctx->sek_len);
+ return(-1);
+ }
+
+ HCRYPT_LOG(LOG_NOTICE, "rekeyed crypto context[%d]\n", (ctx->flags & HCRYPT_CTX_F_xSEK)/2);
+ HCRYPT_PRINTKEY(ctx->sek, ctx->sek_len, "sek");
+
+ /* Regenerate KEK if Password-based (uses newly generated salt and sek_len) */
+ if ((0 < ctx->cfg.pwd_len)
+ && (0 > (iret = hcryptCtx_GenSecret(crypto, ctx)))) {
+ return(iret);
+ }
+
+ /* Assemble the new Keying Material message */
+ if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, ctx, NULL))) {
+ return(iret);
+ }
+ if ((HCRYPT_CTX_S_KEYED <= ctx->alt->status)
+ && hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
+ /*
+ * previous context KM announced in alternate (odd/even) KM,
+ * reassemble it without our KM
+ */
+ hcryptCtx_Tx_AsmKM(crypto, ctx->alt, NULL);
+ }
+
+ /* Initialize the Media Stream message prefix cache */
+ ctx->msg_info->resetCache(ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, ctx->flags & HCRYPT_CTX_F_xSEK);
+ ctx->pkt_cnt = 1;
+
+ ctx->status = HCRYPT_CTX_S_KEYED;
+ return(0);
+}
+
+/*
+ * Refresh the alternate context from the current.
+ * Regenerates the SEK but keep the salt, doing so also
+ * preserve the KEK generated from secret password and salt.
+ */
+
+int hcryptCtx_Tx_Refresh(hcrypt_Session *crypto)
+{
+ hcrypt_Ctx *ctx = crypto->ctx;
+ hcrypt_Ctx *new_ctx;
+ int iret;
+
+ ASSERT(NULL != ctx);
+ ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
+
+ /* Pick the alternative (inactive) context */
+ new_ctx = ctx->alt;
+
+ ASSERT(HCRYPT_CTX_S_SARDY <= new_ctx->status);
+
+ /* Keep same KEK, configuration, and salt */
+ memcpy(&new_ctx->aes_kek, &ctx->aes_kek, sizeof(new_ctx->aes_kek));
+ memcpy(&new_ctx->cfg, &ctx->cfg, sizeof(new_ctx->cfg));
+
+ new_ctx->salt_len = ctx->salt_len;
+ memcpy(new_ctx->salt, ctx->salt, HAICRYPT_SALT_SZ);
+
+ /* Generate new SEK */
+ new_ctx->sek_len = new_ctx->cfg.key_len;
+ if (0 > hcrypt_Prng(new_ctx->sek, new_ctx->sek_len)) {
+ HCRYPT_LOG(LOG_ERR, "PRNG(sek[%zd] failed\n", new_ctx->sek_len);
+ return(-1);
+ }
+ /* Cipher's dependent key */
+ if (crypto->cipher->setkey(crypto->cipher_data, new_ctx, new_ctx->sek, new_ctx->sek_len)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher setkey(sek) failed\n");
+ return(-1);
+ }
+
+ HCRYPT_PRINTKEY(new_ctx->sek, new_ctx->sek_len, "sek");
+
+ /* Assemble the new KMmsg with new and current SEK */
+ if (0 != (iret = hcryptCtx_Tx_AsmKM(crypto, new_ctx, ctx->sek))) {
+ return(iret);
+ }
+
+ /* Initialize the message prefix cache */
+ new_ctx->msg_info->resetCache(new_ctx->MSpfx_cache, HCRYPT_MSG_PT_MS, new_ctx->flags & HCRYPT_MSG_F_xSEK);
+ new_ctx->pkt_cnt = 0;
+
+ new_ctx->status = HCRYPT_CTX_S_KEYED;
+ return(0);
+}
+
+/*
+ * Prepare context switch
+ * both odd & even keys announced
+ */
+int hcryptCtx_Tx_PreSwitch(hcrypt_Session *crypto)
+{
+ hcrypt_Ctx *ctx = crypto->ctx;
+
+ ASSERT(NULL != ctx);
+ ASSERT(HCRYPT_CTX_S_ACTIVE == ctx->status);
+ ASSERT(HCRYPT_CTX_S_KEYED == ctx->alt->status);
+
+ ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE;
+ ctx->alt->flags |= HCRYPT_CTX_F_TTSEND; //Send now
+
+ /* Stop announcing current context if next one contains its key */
+ if (hcryptMsg_KM_HasBothSek(ctx->alt->KMmsg_cache)) {
+ ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
+ }
+ return(0);
+}
+
+int hcryptCtx_Tx_Switch(hcrypt_Session *crypto)
+{
+ hcrypt_Ctx *ctx = crypto->ctx;
+
+ ASSERT(HCRYPT_CTX_S_KEYED <= ctx->alt->status);
+
+ ctx->status = HCRYPT_CTX_S_DEPRECATED;
+ ctx->alt->status = HCRYPT_CTX_S_ACTIVE;
+
+ ctx->alt->flags |= HCRYPT_CTX_F_ANNOUNCE; // Already cleared if new KM has both SEK
+ crypto->ctx = ctx->alt;
+ return(0);
+}
+
+int hcryptCtx_Tx_PostSwitch(hcrypt_Session *crypto)
+{
+ hcrypt_Ctx *ctx = crypto->ctx;
+ hcrypt_Ctx *old_ctx = ctx->alt;
+
+ /* Stop announcing old context (if announced) */
+ old_ctx->flags &= ~HCRYPT_CTX_F_ANNOUNCE;
+ old_ctx->status = HCRYPT_CTX_S_SARDY;
+
+ /* If current context KM announce both, reassemble it */
+ if (hcryptMsg_KM_HasBothSek(ctx->KMmsg_cache)) {
+ hcryptCtx_Tx_AsmKM(crypto, ctx, NULL);
+ }
+ return(0);
+}
+
+/* Assemble Keying Material message */
+int hcryptCtx_Tx_AsmKM(hcrypt_Session *crypto, hcrypt_Ctx *ctx, unsigned char *alt_sek)
+{
+ unsigned char *km_msg;
+ size_t msg_len;
+ int sek_cnt = (NULL == alt_sek ? 1 : 2);
+ unsigned char sek_buf[HAICRYPT_KEY_MAX_SZ * 2];
+ unsigned char *seks;
+
+ if (NULL == ctx) {
+ HCRYPT_LOG(LOG_ERR, "%s", "crypto context undefined\n");
+ return(-1);
+ }
+
+ msg_len = HCRYPT_MSG_KM_OFS_SALT
+ + ctx->salt_len
+ + (ctx->sek_len * sek_cnt)
+ + HAICRYPT_WRAPKEY_SIGN_SZ;
+
+ km_msg = &ctx->KMmsg_cache[0];
+ ctx->KMmsg_len = 0;
+
+ memset(km_msg, 0, msg_len);
+ ctx->msg_info->resetCache(km_msg, HCRYPT_MSG_PT_KM,
+ 2 == sek_cnt ? HCRYPT_MSG_F_xSEK : (ctx->flags & HCRYPT_MSG_F_xSEK));
+
+ /* crypto->KMmsg_cache[4..7]: KEKI=0 */
+ km_msg[HCRYPT_MSG_KM_OFS_CIPHER] = HCRYPT_CIPHER_AES_CTR;
+ km_msg[HCRYPT_MSG_KM_OFS_AUTH] = HCRYPT_AUTH_NONE;
+ km_msg[HCRYPT_MSG_KM_OFS_SE] = crypto->se;
+ hcryptMsg_KM_SetSaltLen(km_msg, ctx->salt_len);
+ hcryptMsg_KM_SetSekLen(km_msg, ctx->sek_len);
+
+ memcpy(&km_msg[HCRYPT_MSG_KM_OFS_SALT], ctx->salt, ctx->salt_len);
+
+ if (2 == sek_cnt) {
+ /* Even SEK first in dual SEK KMmsg */
+ if (HCRYPT_MSG_F_eSEK & ctx->flags) {
+ memcpy(&sek_buf[0], ctx->sek, ctx->sek_len);
+ memcpy(&sek_buf[ctx->sek_len], alt_sek, ctx->sek_len);
+ } else {
+ memcpy(&sek_buf[0], alt_sek, ctx->sek_len);
+ memcpy(&sek_buf[ctx->sek_len], ctx->sek, ctx->sek_len);
+ }
+ seks = sek_buf;
+ } else {
+ seks = ctx->sek;
+ }
+ if (0 > hcrypt_WrapKey(&ctx->aes_kek,
+ &km_msg[HCRYPT_MSG_KM_OFS_SALT + ctx->salt_len],
+ seks, sek_cnt * ctx->sek_len)) {
+
+ HCRYPT_LOG(LOG_ERR, "%s", "wrap key failed\n");
+ return(-1);
+ }
+ ctx->KMmsg_len = msg_len;
+ return(0);
+}
+
+int hcryptCtx_Tx_ManageKM(hcrypt_Session *crypto)
+{
+ hcrypt_Ctx *ctx = crypto->ctx;
+
+ ASSERT(NULL != ctx);
+
+ if ((ctx->pkt_cnt > crypto->km.refresh_rate)
+ || (ctx->pkt_cnt == 0)) { //rolled over
+ /*
+ * End of crypto period for current SEK,
+ * switch to other (even/odd) SEK
+ */
+ HCRYPT_LOG(LOG_INFO, "KM[%d] Activated\n",
+ (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
+
+ hcryptCtx_Tx_Switch(crypto);
+
+ } else
+ if ((ctx->pkt_cnt > (crypto->km.refresh_rate - crypto->km.pre_announce))
+ && !(ctx->alt->flags & HCRYPT_CTX_F_ANNOUNCE)) {
+ /*
+ * End of crypto period approach for this SEK,
+ * prepare next SEK for announcement
+ */
+ hcryptCtx_Tx_Refresh(crypto);
+
+ HCRYPT_LOG(LOG_INFO, "KM[%d] Pre-announced\n",
+ (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
+
+ hcryptCtx_Tx_PreSwitch(crypto);
+
+ } else
+ if ((ctx->alt->status == HCRYPT_CTX_S_DEPRECATED)
+ && (ctx->pkt_cnt > crypto->km.pre_announce)) {
+ /*
+ * Deprecated SEK is no longer needed (for late packets),
+ * decommission it
+ */
+ HCRYPT_LOG(LOG_INFO, "KM[%d] Deprecated\n",
+ (ctx->alt->flags & HCRYPT_CTX_F_xSEK)/2);
+
+ hcryptCtx_Tx_PostSwitch(crypto);
+ }
+
+ /* Check if it is time to send Keying Material */
+ if (timerisset(&crypto->km.tx_period)) { /* tx_period=0.0 -> out-of-stream Keying Material distribution */
+ struct timeval now, nxt_tx;
+
+ gettimeofday(&now, NULL);
+ timeradd(&crypto->km.tx_last, &crypto->km.tx_period, &nxt_tx);
+ if (timercmp(&now, &nxt_tx, >)) {
+ if (crypto->ctx_pair[0].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[0].flags |= HCRYPT_CTX_F_TTSEND;
+ if (crypto->ctx_pair[1].flags & HCRYPT_CTX_F_ANNOUNCE) crypto->ctx_pair[1].flags |= HCRYPT_CTX_F_TTSEND;
+ }
+ }
+
+ return(0);
+}
+
+int hcryptCtx_Tx_InjectKM(hcrypt_Session *crypto,
+ void *out_p[], size_t out_len_p[], int maxout)
+{
+ int i, nbout = 0;
+
+ ASSERT(maxout >= 2);
+ for (i=0; i<2; i++) {
+ if (crypto->ctx_pair[i].flags & HCRYPT_CTX_F_TTSEND) { /* Time To Send */
+ HCRYPT_LOG(LOG_DEBUG, "Send KMmsg[%d] len=%zd\n", i,
+ crypto->ctx_pair[i].KMmsg_len);
+ /* Send Keying Material */
+ out_p[nbout] = crypto->ctx_pair[i].KMmsg_cache;
+ out_len_p[nbout] = crypto->ctx_pair[i].KMmsg_len;
+ nbout++;
+ crypto->ctx_pair[i].flags &= ~HCRYPT_CTX_F_TTSEND;
+ }
+ }
+ if (nbout) {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ crypto->km.tx_last = now;
+ }
+ return(nbout);
+}
+
+
diff --git a/haicrypt/hcrypt_rx.c b/haicrypt/hcrypt_rx.c
new file mode 100644
index 000000000..ffabaca52
--- /dev/null
+++ b/haicrypt/hcrypt_rx.c
@@ -0,0 +1,154 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* NULL */
+#include /* memcmp */
+#include "hcrypt.h"
+
+int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc,
+ unsigned char *in_pfx, unsigned char *data, size_t data_len)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+ int nb = -1;
+
+ if ((NULL == crypto)
+ || (NULL == data)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
+ return(nb);
+ }
+
+ ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_pfx)];
+
+ ASSERT(NULL != ctx); /* Header check should prevent this error */
+ ASSERT(NULL != crypto->cipher); /* Header check should prevent this error */
+
+ crypto->ctx = ctx; /* Context of last received msg */
+ if (NULL == crypto->cipher->decrypt) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher had no decryptor\n");
+ } else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
+ hcrypt_DataDesc indata;
+ indata.pfx = in_pfx;
+ indata.payload = data;
+ indata.len = data_len;
+
+ if (0 > (nb = crypto->cipher->decrypt(crypto->cipher_data, ctx, &indata, 1, NULL, NULL, NULL))) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher failed\n");
+ } else {
+ nb = indata.len;
+ }
+ } else { /* No key received yet */
+ nb = 0;
+ }
+ return(nb);
+}
+
+int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc,
+ unsigned char *in_msg, size_t in_len,
+ void *out_p[], size_t out_len_p[], int maxout)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+ int nbout = maxout;
+ int msg_type;
+
+ if ((NULL == crypto)
+ || (NULL == in_msg)) {
+
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
+ return(-1);
+ }
+
+ /* Validate HaiCrypt message */
+ if (0 > (msg_type = crypto->msg_info->parseMsg(in_msg))) {
+ return(-1);
+ }
+
+ switch(msg_type) {
+ case HCRYPT_MSG_PT_MS: /* MSmsg */
+ ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
+
+ if ((NULL == out_p)
+ || (NULL == out_len_p)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid parameters\n");
+ return(-1);
+ }
+ ASSERT(NULL != ctx); /* Header check should prevent this error */
+ ASSERT(NULL != crypto->cipher); /* Header check should prevent this error */
+
+ crypto->ctx = ctx; /* Context of last received msg */
+ if (NULL == crypto->cipher->decrypt) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher had no decryptor\n");
+ nbout = -1;
+ } else if (ctx->status >= HCRYPT_CTX_S_KEYED) {
+ hcrypt_DataDesc indata;
+ indata.pfx = in_msg;
+ indata.payload = &in_msg[crypto->msg_info->pfx_len];
+ indata.len = in_len - crypto->msg_info->pfx_len;
+
+ if (crypto->cipher->decrypt(crypto->cipher_data, ctx, &indata, 1, out_p, out_len_p, &nbout)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "cipher failed\n");
+ nbout = -1;
+ }
+ } else { /* No key received yet */
+ nbout = 0;
+ }
+ break;
+
+ case HCRYPT_MSG_PT_KM: /* KMmsg */
+ /* Even or Both SEKs check with even context */
+ ctx = &crypto->ctx_pair[hcryptMsg_GetKeyIndex(crypto->msg_info, in_msg)];
+
+ ASSERT(NULL != ctx); /* Header check should prevent this error */
+
+ if ((ctx->status < HCRYPT_CTX_S_KEYED) /* No key deciphered yet */
+ || (in_len != ctx->KMmsg_len) /* or not same size */
+ || (0 != memcmp(ctx->KMmsg_cache, in_msg, in_len))) { /* or different */
+
+ nbout = hcryptCtx_Rx_ParseKM(crypto, in_msg, in_len);
+ //-2: unmatched shared secret
+ //-1: other failures
+ //0: success
+ } else {
+ nbout = 0;
+ }
+ if (NULL != out_p) out_p[0] = NULL;
+ if (NULL != out_len_p) out_len_p[0] = 0;
+ break;
+
+ default:
+ HCRYPT_LOG(LOG_WARNING, "%s", "unknown packet type\n");
+ nbout = 0;
+ break;
+ }
+
+ return(nbout);
+}
+
+
diff --git a/haicrypt/hcrypt_sa.c b/haicrypt/hcrypt_sa.c
new file mode 100644
index 000000000..cac58dceb
--- /dev/null
+++ b/haicrypt/hcrypt_sa.c
@@ -0,0 +1,112 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+*****************************************************************************/
+
+/*
+ * For now:
+ * Pre-shared or password derived KEK (Key Encrypting Key)
+ * Future:
+ * Certificate-based association
+ */
+#include /* memcpy */
+#include "hcrypt.h"
+
+int hcryptCtx_SetSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx, HaiCrypt_Secret *secret)
+{
+ int iret;
+ (void)crypto;
+
+ switch(secret->typ) {
+ case HAICRYPT_SECTYP_PRESHARED:
+ ASSERT(secret->len <= HAICRYPT_KEY_MAX_SZ);
+ ctx->cfg.pwd_len = 0;
+ /* KEK: Key Encrypting Key */
+ if (HCRYPT_CTX_F_ENCRYPT & ctx->flags) {
+ iret = AES_set_encrypt_key(secret->str, secret->len * 8, &ctx->aes_kek);
+ } else {
+ iret = AES_set_decrypt_key(secret->str, secret->len * 8, &ctx->aes_kek);
+ }
+ if (0 > iret) {
+ HCRYPT_LOG(LOG_ERR, "AES_set_%s_key(kek[%zd]) failed (rc=%d)\n",
+ HCRYPT_CTX_F_ENCRYPT & ctx->flags ? "encrypt" : "decrypt",
+ secret->len, iret);
+ return(-1);
+ }
+ ctx->status = HCRYPT_CTX_S_SARDY;
+ break;
+
+ case HAICRYPT_SECTYP_PASSPHRASE:
+ ASSERT(secret->len <= sizeof(ctx->cfg.pwd));
+ memcpy(ctx->cfg.pwd, secret->str, secret->len);
+ ctx->cfg.pwd_len = secret->len;
+ /* KEK will be derived from password with Salt */
+ ctx->status = HCRYPT_CTX_S_SARDY;
+ break;
+
+ default:
+ HCRYPT_LOG(LOG_ERR, "Unknown secret type %d\n",
+ secret->typ);
+ return(-1);
+ }
+ return(0);
+}
+
+int hcryptCtx_GenSecret(hcrypt_Session *crypto, hcrypt_Ctx *ctx)
+{
+ /*
+ * KEK need same length as the key it protects (SEK)
+ * KEK = PBKDF2(Pwd, LSB(64, Salt), Iter, Klen)
+ */
+ unsigned char kek[HAICRYPT_KEY_MAX_SZ];
+ size_t kek_len = ctx->sek_len;
+ size_t pbkdf_salt_len = (ctx->salt_len >= HAICRYPT_PBKDF2_SALT_LEN
+ ? HAICRYPT_PBKDF2_SALT_LEN
+ : ctx->salt_len);
+ int iret;
+ (void)crypto;
+
+ PKCS5_PBKDF2_HMAC_SHA1(ctx->cfg.pwd, ctx->cfg.pwd_len,
+ &ctx->salt[ctx->salt_len - pbkdf_salt_len], pbkdf_salt_len,
+ HAICRYPT_PBKDF2_ITER_CNT, kek_len, kek);
+
+ HCRYPT_PRINTKEY(ctx->cfg.pwd, ctx->cfg.pwd_len, "pwd");
+ HCRYPT_PRINTKEY(kek, kek_len, "kek");
+
+ /* KEK: Key Encrypting Key */
+ if (HCRYPT_CTX_F_ENCRYPT & ctx->flags) {
+ if (0 > (iret = AES_set_encrypt_key(kek, kek_len * 8, &ctx->aes_kek))) {
+ HCRYPT_LOG(LOG_ERR, "AES_set_encrypt_key(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret);
+ return(-1);
+ }
+ } else {
+ if (0 > (iret = AES_set_decrypt_key(kek, kek_len * 8, &ctx->aes_kek))) {
+ HCRYPT_LOG(LOG_ERR, "AES_set_decrypt_key(pdkek[%zd]) failed (rc=%d)\n", kek_len, iret);
+ return(-1);
+ }
+ }
+ return(0);
+}
+
diff --git a/haicrypt/hcrypt_tx.c b/haicrypt/hcrypt_tx.c
new file mode 100644
index 000000000..d42d4cac3
--- /dev/null
+++ b/haicrypt/hcrypt_tx.c
@@ -0,0 +1,190 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include
+#include /* NULL */
+#include /* memcpy */
+#ifdef WIN32
+ #include
+ #include
+ #include
+#else
+ #include /* htonl */
+#endif
+#include "hcrypt.h"
+
+int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_pp)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ int pad_factor = (HCRYPT_CTX_MODE_AESECB == crypto->ctx->mode ? 128/8 : 1);
+
+ ASSERT(NULL != crypto);
+ ASSERT(NULL != crypto->cipher);
+
+ if (NULL != crypto->cipher->getinbuf) {
+ ASSERT(NULL != crypto->cipher_data);
+ if (0 >= crypto->cipher->getinbuf(crypto->cipher_data, crypto->msg_info->pfx_len,
+ data_len, pad_factor, in_pp)) {
+ *in_pp = NULL;
+ return(-1);
+ }
+ } else {
+#ifndef WIN32
+ ASSERT(crypto->inbuf != NULL);
+#endif
+ size_t in_len = crypto->msg_info->pfx_len + hcryptMsg_PaddedLen(data_len, pad_factor);
+ *in_pp = crypto->inbuf;
+ if (in_len > crypto->inbuf_siz) {
+ *in_pp = NULL;
+ return(-1);
+ }
+ }
+ return(crypto->msg_info->pfx_len);
+}
+
+int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+ int nbout = 0;
+
+ if ((NULL == crypto)
+ || (NULL == crypto->ctx)
+ || (NULL == out_p)
+ || (NULL == out_len_p)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid params\n");
+ return(-1);
+ }
+
+ /* Manage Key Material (refresh, announce, decommission) */
+ hcryptCtx_Tx_ManageKM(crypto);
+
+ if (NULL == (ctx = crypto->ctx)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
+ return(-1);
+ }
+ ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
+
+ nbout = hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
+ return(nbout);
+}
+
+int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+
+ if ((NULL == crypto)
+ || (NULL == (ctx = crypto->ctx))){
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid params\n");
+ return(-1);
+ }
+ return(hcryptCtx_GetKeyFlags(ctx));
+}
+
+int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc,
+ unsigned char *in_pfx, unsigned char *in_data, size_t in_len)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+ int nbout = 0;
+
+ if ((NULL == crypto)
+ || (NULL == (ctx = crypto->ctx))){
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid params\n");
+ return(-1);
+ }
+ /* Get/Set packet index */
+ ctx->msg_info->indexMsg(in_pfx, ctx->MSpfx_cache);
+
+ /* Encrypt */
+ {
+ hcrypt_DataDesc indata;
+ indata.pfx = in_pfx;
+ indata.payload = in_data;
+ indata.len = in_len;
+
+ if (0 > (nbout = crypto->cipher->encrypt(crypto->cipher_data, ctx, &indata, 1, NULL, NULL, NULL))) {
+ HCRYPT_LOG(LOG_ERR, "%s", "encrypt failed\n");
+ return(nbout);
+ }
+ }
+ ctx->pkt_cnt++;
+
+ return(nbout);
+}
+
+int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc,
+ unsigned char *in_msg, size_t in_len,
+ void *out_p[], size_t out_len_p[], int maxout)
+{
+ hcrypt_Session *crypto = (hcrypt_Session *)hhc;
+ hcrypt_Ctx *ctx;
+ int nb, nbout = 0;
+
+ if ((NULL == crypto)
+ || (NULL == crypto->ctx)
+ || (NULL == out_p)
+ || (NULL == out_len_p)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "invalid params\n");
+ return(-1);
+ }
+
+ /* Manage Key Material (refresh, announce, decommission) */
+ hcryptCtx_Tx_ManageKM(crypto);
+
+ if (NULL == (ctx = crypto->ctx)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "crypto context not defined\n");
+ return(-1);
+ }
+ ASSERT(ctx->status == HCRYPT_CTX_S_ACTIVE);
+
+ nbout += hcryptCtx_Tx_InjectKM(crypto, out_p, out_len_p, maxout);
+
+ /* Get packet index */
+ ctx->msg_info->indexMsg(in_msg, ctx->MSpfx_cache);
+
+ /* Encrypt */
+ nb = maxout - nbout;
+ {
+ hcrypt_DataDesc indata;
+ indata.pfx = in_msg;
+ indata.payload = &in_msg[ctx->msg_info->pfx_len];
+ indata.len = in_len - ctx->msg_info->pfx_len;
+
+ if (crypto->cipher->encrypt(crypto->cipher_data, ctx, &indata, 1, &out_p[nbout], &out_len_p[nbout], &nb)) {
+ HCRYPT_LOG(LOG_ERR, "%s", "encrypt failed\n");
+ return(nbout);
+ }
+ }
+ nbout += nb;
+ ctx->pkt_cnt++;
+
+ return(nbout);
+}
diff --git a/haicrypt/hcrypt_ut.c b/haicrypt/hcrypt_ut.c
new file mode 100644
index 000000000..2e1599b3e
--- /dev/null
+++ b/haicrypt/hcrypt_ut.c
@@ -0,0 +1,231 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-07-11 (jdube)
+ HaiCrypt initial implementation.
+*****************************************************************************/
+
+#include /* memcpy */
+#include
+#include "hcrypt.h"
+
+#ifndef WIN32
+
+/* RFC6070 PBKDF2 Tests Vectors */
+
+static struct TestVector {
+ size_t pwd_len;
+ const char *pwd;
+ size_t salt_len;
+ const unsigned char *salt;
+ int cnt;
+ size_t dk_len;
+ unsigned char dk[32];
+} tv[] = {
+ { /* 1 */
+ .pwd_len = 8, .pwd = "password",
+ .salt_len = 4, .salt = (unsigned char *)"salt",
+ .cnt = 1,
+ .dk_len = 20,
+ .dk = {
+ 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+ 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+ 0x2f, 0xe0, 0x37, 0xa6
+ }
+ },
+ { /* 2 */
+ .pwd_len = 8, .pwd = "password",
+ .salt_len = 4, .salt = (unsigned char *)"salt",
+ .cnt = 2,
+ .dk_len = 20,
+ .dk = {
+ 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+ 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+ 0xd8, 0xde, 0x89, 0x57
+ }
+ },
+ { /* 3 */
+ .pwd_len = 8, .pwd = "password",
+ .salt_len = 4, .salt = (unsigned char *)"salt",
+ .cnt = 4096,
+ .dk_len = 20,
+ .dk = {
+ 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+ 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+ 0x65, 0xa4, 0x29, 0xc1
+ }
+ },
+ { /* 4 */
+ .pwd_len = 8, .pwd = "password",
+ .salt_len = 4, .salt = (unsigned char *)"salt",
+ .cnt = 16777216,
+ .dk_len = 20,
+ .dk = {
+ 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
+ 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
+ 0x26, 0x34, 0xe9, 0x84
+ }
+ },
+ { /* 5 */
+ .pwd_len = 24, .pwd = "passwordPASSWORDpassword",
+ .salt_len = 36, .salt = (unsigned char *)"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+ .cnt = 4096,
+ .dk_len = 25,
+ .dk = {
+ 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+ 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+ 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
+ 0x38
+ }
+ },
+ { /* 6 */
+ .pwd_len = 9, .pwd = "pass\0word",
+ .salt_len = 5, .salt = (unsigned char *)"sa\0lt",
+ .cnt = 4096,
+ .dk_len = 16,
+ .dk = {
+ 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+ 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
+ }
+ },
+};
+
+#include
+
+static int hc_ut_pbkdf2(unsigned verbose)
+{
+ int i;
+ int nbt = sizeof(tv)/sizeof(tv[0]);
+ int nbe = 0;
+ unsigned char dk[32];
+ struct timeval tstart, tstop, tdiff;
+
+ for (i=0; i HaiCrypt_Tx_Data(hcrypto, &pkt[0], &pkt[16], UT_PKTSZ)) nbe++;
+ if (0 == (i % 1000)) {
+ printf("\b\b\b\b\b\b%6d", i);
+ fflush(stdout);
+ }
+ }
+ gettimeofday(&tstop, NULL);
+ timersub(&tstop, &tstart, &tdiff);
+ printf("\nhaicrypt: encrypted %ld packets in %lu.%06lu sec (%ld.%03ld kbps)\n",
+ UT_NBPKTS, tdiff.tv_sec, (unsigned long)tdiff.tv_usec,
+ (((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) / 1000),
+ (((UT_NBPKTS * UT_PKTSZ*10)/((tdiff.tv_sec*10) + (tdiff.tv_usec/100))) % 1000));
+
+ HaiCrypt_Close(hcrypto);
+ return(nbe);
+}
+
+int main(int argc, char *argv[])
+{
+
+ int nbe = 0;
+ (void)argc;
+ (void)argv;
+ nbe += hc_ut_encrypt_ctr_speed();
+ nbe += hc_ut_pbkdf2(1);
+
+ printf("haicrypt unit test %s: %d errors found\n", nbe ? "failed" : "passed", nbe);
+ return(nbe);
+}
+
+#endif // WIN32
diff --git a/haicrypt/hcrypt_xpt_srt.c b/haicrypt/hcrypt_xpt_srt.c
new file mode 100644
index 000000000..f9c98c097
--- /dev/null
+++ b/haicrypt/hcrypt_xpt_srt.c
@@ -0,0 +1,179 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* memset, memcpy */
+#ifdef WIN32
+ #include
+ #include
+#else
+ #include /* htonl, ntohl */
+#endif
+#include "hcrypt.h"
+
+/*
+ * HaiCrypt SRT (Secure Reliable Transport) Media Stream (MS) Msg Prefix:
+ * This is UDT data header with Crypto Key Flags (KF) added.
+ * Header is in 32bit host order words in the context of the functions of this handler.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x00 |0| Packet Sequence Number (pki) |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x04 |FF |o|KF | Message Number |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x08 | Time Stamp |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x0C | Destination Socket ID) |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * | Payload... |
+ */
+
+
+/*
+ * HaiCrypt Standalone Transport Keying Material (KM) Msg header kept in SRT
+ * Message and cache maintained in network order
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x00 |0|Vers | PT | Sign | resv |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * ... .
+ */
+
+#define HCRYPT_MSG_SRT_HDR_SZ 16
+#define HCRYPT_MSG_SRT_PFX_SZ 16
+
+#define HCRYPT_MSG_SRT_OFS_PKI 0
+#define HCRYPT_MSG_SRT_OFS_MSGNO 4
+#define HCRYPT_MSG_SRT_SHF_KFLGS 27 //shift
+
+static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
+
+static unsigned hcryptMsg_SRT_GetKeyFlags(unsigned char *msg)
+{
+ uint32_t msgno;
+ memcpy(&msgno, &msg[HCRYPT_MSG_SRT_OFS_MSGNO], sizeof(msgno)); //header is in host order
+ return((unsigned)((msgno >> HCRYPT_MSG_SRT_SHF_KFLGS) & HCRYPT_MSG_F_xSEK));
+}
+
+static hcrypt_Pki hcryptMsg_SRT_GetPki(unsigned char *msg, int nwkorder)
+{
+ hcrypt_Pki pki;
+ memcpy(&pki, &msg[HCRYPT_MSG_SRT_OFS_PKI], sizeof(pki)); //header is in host order
+ return (nwkorder ? htonl(pki) : pki);
+}
+
+static void hcryptMsg_SRT_SetPki(unsigned char *msg, hcrypt_Pki pki)
+{
+ memcpy(&msg[HCRYPT_MSG_SRT_OFS_PKI], &pki, sizeof(pki)); //header is in host order
+}
+
+static void hcryptMsg_SRT_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
+{
+ switch(pkt_type) {
+ case HCRYPT_MSG_PT_MS: /* Media Stream */
+ /* Nothing to do, header filled by protocol */
+ break;
+ case HCRYPT_MSG_PT_KM: /* Keying Material */
+ pfx_cache[HCRYPT_MSG_KM_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
+ pfx_cache[HCRYPT_MSG_KM_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
+ pfx_cache[HCRYPT_MSG_KM_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
+ pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
+ break;
+ default:
+ break;
+ }
+}
+
+static void hcryptMsg_SRT_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
+{
+ (void)msg;
+ (void)pfx_cache;
+ return; //nothing to do, header and index maintained by SRT
+}
+
+static int hcryptMsg_SRT_ParseMsg(unsigned char *msg)
+{
+ int rc;
+
+ if ((HCRYPT_MSG_VERSION == hcryptMsg_KM_GetVersion(msg)) /* Version 1 */
+ && (HCRYPT_MSG_PT_KM == hcryptMsg_KM_GetPktType(msg)) /* Keying Material */
+ && (HCRYPT_MSG_SIGN == hcryptMsg_KM_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
+ rc = HCRYPT_MSG_PT_KM;
+ } else {
+ //Assume it's data.
+ //SRT does not call this for MS msg
+ rc = HCRYPT_MSG_PT_MS;
+ }
+
+ switch(rc) {
+ case HCRYPT_MSG_PT_MS:
+ if (hcryptMsg_HasNoSek(&_hcMsg_SRT_MsgInfo, msg)
+ || hcryptMsg_HasBothSek(&_hcMsg_SRT_MsgInfo, msg)) {
+ HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
+ hcryptMsg_GetKeyIndex(&_hcMsg_SRT_MsgInfo, msg));
+ return(-1);
+ }
+ break;
+ case HCRYPT_MSG_PT_KM:
+ if (HCRYPT_SE_TSSRT != hcryptMsg_KM_GetSE(msg)) { //Check Stream Encapsulation (SE)
+ HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
+ hcryptMsg_KM_GetSE(msg));
+ return(-1);
+ }
+ if (hcryptMsg_KM_HasNoSek(msg)) {
+ HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
+ hcryptMsg_KM_GetKeyIndex(msg));
+ return(-1);
+ }
+ break;
+ default:
+ HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
+ rc = 0; /* unknown packet type */
+ break;
+ }
+ return(rc); /* -1: error, 0: unknown: >0: PT */
+}
+
+static hcrypt_MsgInfo _hcMsg_SRT_MsgInfo;
+
+hcrypt_MsgInfo *hcryptMsg_SRT_MsgInfo(void)
+{
+ _hcMsg_SRT_MsgInfo.hdr_len = HCRYPT_MSG_SRT_HDR_SZ;
+ _hcMsg_SRT_MsgInfo.pfx_len = HCRYPT_MSG_SRT_PFX_SZ;
+ _hcMsg_SRT_MsgInfo.getKeyFlags = hcryptMsg_SRT_GetKeyFlags;
+ _hcMsg_SRT_MsgInfo.getPki = hcryptMsg_SRT_GetPki;
+ _hcMsg_SRT_MsgInfo.setPki = hcryptMsg_SRT_SetPki;
+ _hcMsg_SRT_MsgInfo.resetCache = hcryptMsg_SRT_ResetCache;
+ _hcMsg_SRT_MsgInfo.indexMsg = hcryptMsg_SRT_IndexMsg;
+ _hcMsg_SRT_MsgInfo.parseMsg = hcryptMsg_SRT_ParseMsg;
+
+ return(&_hcMsg_SRT_MsgInfo);
+}
+
diff --git a/haicrypt/hcrypt_xpt_sta.c b/haicrypt/hcrypt_xpt_sta.c
new file mode 100644
index 000000000..fcfaa4b53
--- /dev/null
+++ b/haicrypt/hcrypt_xpt_sta.c
@@ -0,0 +1,187 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#include /* memset, memcpy */
+#ifdef WIN32
+ #include
+ #include
+#else
+ #include /* htonl, ntohl */
+#endif
+#include "hcrypt.h"
+
+/*
+ * HaiCrypt Standalone Transport Media Stream (MS) Data Msg Prefix:
+ * Cache maintained in network order
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x00 |0|Vers | PT | Sign | resv |KF |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x04 | pki |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * | payload... |
+ */
+
+/*
+ * HaiCrypt Standalone Transport Keying Material (KM) Msg (no prefix, use KM Msg directly):
+ * Cache maintained in network order
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * 0x00 |0|Vers | PT | Sign | resv |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * ... .
+ */
+
+#define HCRYPT_MSG_STA_HDR_SZ 4
+#define HCRYPT_MSG_STA_PKI_SZ 4
+#define HCRYPT_MSG_STA_PFX_SZ (HCRYPT_MSG_STA_HDR_SZ + HCRYPT_MSG_STA_PKI_SZ)
+
+#define HCRYPT_MSG_STA_OFS_VERSION HCRYPT_MSG_KM_OFS_VERSION
+#define HCRYPT_MSG_STA_OFS_PT HCRYPT_MSG_KM_OFS_PT
+#define HCRYPT_MSG_STA_OFS_SIGN HCRYPT_MSG_KM_OFS_SIGN
+#define HCRYPT_MSG_STA_OFS_KFLGS HCRYPT_MSG_KM_OFS_KFLGS
+
+#define HCRYPT_MSG_STA_OFS_PKI HCRYPT_MSG_STA_HDR_SZ
+
+#define hcryptMsg_STA_GetVersion(msg) (((msg)[HCRYPT_MSG_STA_OFS_VERSION]>>4)& 0xF)
+#define hcryptMsg_STA_GetPktType(msg) (((msg)[HCRYPT_MSG_STA_OFS_PT]) & 0xF)
+#define hcryptMsg_STA_GetSign(msg) (((msg)[HCRYPT_MSG_STA_OFS_SIGN]<<8) | (msg)[HCRYPT_MSG_STA_OFS_SIGN+1])
+
+static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
+
+static unsigned hcryptMsg_STA_GetKeyFlags(unsigned char *msg)
+{
+ return((unsigned)(msg[HCRYPT_MSG_STA_OFS_KFLGS] & HCRYPT_MSG_F_xSEK));
+}
+
+static hcrypt_Pki hcryptMsg_STA_GetPki(unsigned char *msg, int nwkorder)
+{
+ hcrypt_Pki pki;
+ memcpy(&pki, &msg[HCRYPT_MSG_STA_OFS_PKI], sizeof(pki)); //header is in host order
+ return (nwkorder ? pki : ntohl(pki));
+}
+
+static void hcryptMsg_STA_SetPki(unsigned char *msg, hcrypt_Pki pki)
+{
+ hcrypt_Pki nwk_pki = htonl(pki);
+ memcpy(&msg[HCRYPT_MSG_STA_OFS_PKI], &nwk_pki, sizeof(nwk_pki)); //header is in host order
+}
+
+static void hcryptMsg_STA_ResetCache(unsigned char *pfx_cache, unsigned pkt_type, unsigned kflgs)
+{
+ pfx_cache[HCRYPT_MSG_STA_OFS_VERSION] = (unsigned char)((HCRYPT_MSG_VERSION << 4) | pkt_type); // version || PT
+ pfx_cache[HCRYPT_MSG_STA_OFS_SIGN] = (unsigned char)((HCRYPT_MSG_SIGN >> 8) & 0xFF); // Haivision PnP Mfr ID
+ pfx_cache[HCRYPT_MSG_STA_OFS_SIGN+1] = (unsigned char)(HCRYPT_MSG_SIGN & 0xFF);
+
+ switch(pkt_type) {
+ case HCRYPT_MSG_PT_MS:
+ pfx_cache[HCRYPT_MSG_STA_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
+ hcryptMsg_STA_SetPki(pfx_cache, 0);
+ break;
+ case HCRYPT_MSG_PT_KM:
+ pfx_cache[HCRYPT_MSG_KM_OFS_KFLGS] = (unsigned char)kflgs; //HCRYPT_MSG_F_xxx
+ break;
+ default:
+ break;
+ }
+}
+
+static void hcryptMsg_STA_IndexMsg(unsigned char *msg, unsigned char *pfx_cache)
+{
+ hcrypt_Pki pki = hcryptMsg_STA_GetPki(pfx_cache, 0); //Get in host order
+ memcpy(msg, pfx_cache, HCRYPT_MSG_STA_PFX_SZ);
+ hcryptMsg_SetPki(&_hcMsg_STA_MsgInfo, pfx_cache, ++pki);
+}
+
+static time_t _tLastLogTime = 0;
+
+static int hcryptMsg_STA_ParseMsg(unsigned char *msg)
+{
+ int rc;
+
+ if ((HCRYPT_MSG_VERSION != hcryptMsg_STA_GetVersion(msg)) /* Version 1 */
+ || (HCRYPT_MSG_SIGN != hcryptMsg_STA_GetSign(msg))) { /* 'HAI' PnP Mfr ID */
+ time_t tCurrentTime = time(NULL);
+ // invalid data
+ if ((tCurrentTime - _tLastLogTime) >= 2 || (0 == _tLastLogTime))
+ {
+ _tLastLogTime = tCurrentTime;
+ HCRYPT_LOG(LOG_ERR, "invalid msg hdr: 0x%02x %02x%02x %02x\n",
+ msg[0], msg[1], msg[2], msg[3]);
+ }
+ return(-1); /* Invalid packet */
+ }
+ rc = hcryptMsg_STA_GetPktType(msg);
+ switch(rc) {
+ case HCRYPT_MSG_PT_MS:
+ if (hcryptMsg_HasNoSek(&_hcMsg_STA_MsgInfo, msg)
+ || hcryptMsg_HasBothSek(&_hcMsg_STA_MsgInfo, msg)) {
+ HCRYPT_LOG(LOG_ERR, "invalid MS msg flgs: %02x\n",
+ hcryptMsg_GetKeyIndex(&_hcMsg_STA_MsgInfo, msg));
+ return(-1);
+ }
+ break;
+ case HCRYPT_MSG_PT_KM:
+ if (HCRYPT_SE_TSUDP != hcryptMsg_KM_GetSE(msg)) {
+ HCRYPT_LOG(LOG_ERR, "invalid KM msg SE: %d\n",
+ hcryptMsg_KM_GetSE(msg));
+ } else if (hcryptMsg_KM_HasNoSek(msg)) {
+ HCRYPT_LOG(LOG_ERR, "invalid KM msg flgs: %02x\n",
+ hcryptMsg_KM_GetKeyIndex(msg));
+ return(-1);
+ }
+ break;
+ default:
+ HCRYPT_LOG(LOG_ERR, "invalid pkt type: %d\n", rc);
+ rc = 0; /* unknown packet type */
+ break;
+ }
+ return(rc); /* -1: error, 0: unknown: >0: PT */
+}
+
+static hcrypt_MsgInfo _hcMsg_STA_MsgInfo;
+
+hcrypt_MsgInfo *hcryptMsg_STA_MsgInfo(void)
+{
+ _hcMsg_STA_MsgInfo.hdr_len = HCRYPT_MSG_STA_HDR_SZ;
+ _hcMsg_STA_MsgInfo.pfx_len = HCRYPT_MSG_STA_PFX_SZ;
+ _hcMsg_STA_MsgInfo.getKeyFlags = hcryptMsg_STA_GetKeyFlags;
+ _hcMsg_STA_MsgInfo.getPki = hcryptMsg_STA_GetPki;
+ _hcMsg_STA_MsgInfo.setPki = hcryptMsg_STA_SetPki;
+ _hcMsg_STA_MsgInfo.resetCache = hcryptMsg_STA_ResetCache;
+ _hcMsg_STA_MsgInfo.indexMsg = hcryptMsg_STA_IndexMsg;
+ _hcMsg_STA_MsgInfo.parseMsg = hcryptMsg_STA_ParseMsg;
+
+ return(&_hcMsg_STA_MsgInfo);
+}
+
diff --git a/include/haicrypt.h b/include/haicrypt.h
new file mode 100644
index 000000000..7b895cfcd
--- /dev/null
+++ b/include/haicrypt.h
@@ -0,0 +1,125 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#ifndef HAICRYPT_H
+#define HAICRYPT_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define (in Makefile) the HaiCrypt ciphers compiled in
+ */
+//#define HAICRYPT_USE_OPENSSL_EVP 1 /* Preferred for most cases */
+//#define HAICRYPT_USE_OPENSSL_AES 1 /* Mandatory for key wrapping and prng */
+
+typedef void *HaiCrypt_Cipher;
+
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP(void); /* OpenSSL EVP interface (default to EVP_CTR) */
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CBC(void); /* OpenSSL EVP interface for AES-CBC */
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_EVP_CTR(void); /* OpenSSL EVP interface for AES-CTR */
+HaiCrypt_Cipher HaiCryptCipher_OpenSSL_AES(void); /* OpenSSL AES direct interface */
+
+
+#define HAICRYPT_CIPHER_BLK_SZ 16 /* AES Block Size */
+
+#define HAICRYPT_PWD_MAX_SZ 80 /* MAX password (for Password-based Key Derivation) */
+#define HAICRYPT_KEY_MAX_SZ 32 /* MAX key */
+#define HAICRYPT_SECRET_MAX_SZ (HAICRYPT_PWD_MAX_SZ > HAICRYPT_KEY_MAX_SZ ? HAICRYPT_PWD_MAX_SZ : HAICRYPT_KEY_MAX_SZ)
+
+
+#define HAICRYPT_SALT_SZ 16
+
+#define HAICRYPT_WRAPKEY_SIGN_SZ 8 /* RFC3394 AES KeyWrap signature size */
+
+#define HAICRYPT_PBKDF2_SALT_LEN 8 /* PKCS#5 PBKDF2 Password based key derivation salt length */
+#define HAICRYPT_PBKDF2_ITER_CNT 2048 /* PKCS#5 PBKDF2 Password based key derivation iteration count */
+
+#define HAICRYPT_TS_PKT_SZ 188 /* Transport Stream packet size */
+
+typedef struct {
+#define HAICRYPT_SECTYP_UNDEF 0
+#define HAICRYPT_SECTYP_PRESHARED 1 /* Preshared KEK */
+#define HAICRYPT_SECTYP_PASSPHRASE 2 /* Password */
+ unsigned typ;
+ size_t len;
+ unsigned char str[HAICRYPT_SECRET_MAX_SZ];
+}HaiCrypt_Secret;
+
+typedef struct {
+#define HAICRYPT_CFG_F_TX 0x01 /* !TX -> RX */
+#define HAICRYPT_CFG_F_CRYPTO 0x02 /* Perform crypto Tx:Encrypt Rx:Decrypt */
+#define HAICRYPT_CFG_F_FEC 0x04 /* Do Forward Error Correction */
+ unsigned flags;
+
+ HaiCrypt_Secret secret; /* Security Association */
+
+ HaiCrypt_Cipher cipher; /* Media Stream cipher implementation */
+#define HAICRYPT_DEF_KEY_LENGTH 16 /* default key length (bytes) */
+ size_t key_len; /* SEK length (bytes) */
+#define HAICRYPT_DEF_DATA_MAX_LENGTH 1500 /* default packet data length (bytes) */
+ size_t data_max_len; /* Maximum data_len passed to HaiCrypt (bytes) */
+
+#define HAICRYPT_XPT_STANDALONE 0
+#define HAICRYPT_XPT_SRT 1
+ int xport;
+
+#define HAICRYPT_DEF_KM_TX_PERIOD 1000 /* Keying Material Default Tx Period (msec) */
+ unsigned int km_tx_period_ms; /* Keying Material Tx period (msec) */
+#define HAICRYPT_DEF_KM_REFRESH_RATE 0x1000000 /* Keying Material Default Refresh Rate (pkts) */
+ unsigned int km_refresh_rate_pkt; /* Keying Material Refresh Rate (pkts) */
+#define HAICRYPT_DEF_KM_PRE_ANNOUNCE 0x1000 /* Keying Material Default Pre/Post Announce (pkts) */
+ unsigned int km_pre_announce_pkt; /* Keying Material Pre/Post Announce (pkts) */
+}HaiCrypt_Cfg;
+
+typedef void *HaiCrypt_Handle;
+
+int HaiCrypt_SetLogLevel(int level, int logfa);
+
+int HaiCrypt_Create(HaiCrypt_Cfg *cfg, HaiCrypt_Handle *phhc);
+int HaiCrypt_Close(HaiCrypt_Handle hhc);
+int HaiCrypt_Tx_GetBuf(HaiCrypt_Handle hhc, size_t data_len, unsigned char **in_p);
+int HaiCrypt_Tx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
+ void *out_p[], size_t out_len_p[], int maxout);
+int HaiCrypt_Rx_Process(HaiCrypt_Handle hhc, unsigned char *in, size_t in_len,
+ void *out_p[], size_t out_len_p[], int maxout);
+
+int HaiCrypt_Tx_GetKeyFlags(HaiCrypt_Handle hhc);
+int HaiCrypt_Tx_ManageKeys(HaiCrypt_Handle hhc, void *out_p[], size_t out_len_p[], int maxout);
+int HaiCrypt_Tx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
+int HaiCrypt_Rx_Data(HaiCrypt_Handle hhc, unsigned char *pfx, unsigned char *data, size_t data_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAICRYPT_H */
diff --git a/include/hcrypt_ctx.h b/include/hcrypt_ctx.h
new file mode 100644
index 000000000..f734302ad
--- /dev/null
+++ b/include/hcrypt_ctx.h
@@ -0,0 +1,166 @@
+/*
+ * SRT - Secure, Reliable, Transport
+ * Copyright (c) 2017 Haivision Systems Inc.
+ *
+ * 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, see
+ */
+
+
+/*****************************************************************************
+written by
+ Haivision Systems Inc.
+
+ 2011-06-23 (jdube)
+ HaiCrypt initial implementation.
+ 2014-03-11 (jdube)
+ Adaptation for SRT.
+*****************************************************************************/
+
+#ifndef HCRYPT_CTX_H
+#define HCRYPT_CTX_H
+
+#include
+#include